From 71e74d447ec92029f10560d9671d418ccc483d96 Mon Sep 17 00:00:00 2001 From: xiwa Date: Thu, 8 Feb 2024 23:47:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=8A=A0=E5=85=A5spring-brick=E5=92=8Cplu?= =?UTF-8?q?gin-core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iot-common-core/pom.xml | 2 +- iot-common-doc/pom.xml | 2 +- iot-common-excel/pom.xml | 7 +- iot-common-log/pom.xml | 2 +- iot-common-oss/pom.xml | 2 +- iot-common-redis/pom.xml | 2 +- iot-common-satoken/pom.xml | 2 +- iot-common-tenant/pom.xml | 2 +- iot-common-thing/pom.xml | 2 +- iot-common-web/pom.xml | 2 +- iot-common-websocket/pom.xml | 2 +- iot-message-bus/iot-message-core/pom.xml | 2 +- iot-message-bus/iot-message-event-bus/pom.xml | 2 +- iot-message-bus/iot-message-rocketmq/pom.xml | 2 +- iot-message-bus/pom.xml | 2 +- iot-plugin-core/pom.xml | 55 ++ .../java/cc/iotkit/plugin/core/IPlugin.java | 22 + .../cc/iotkit/plugin/core/IPluginConfig.java | 21 + .../cc/iotkit/plugin/core/IPluginScript.java | 27 + .../iotkit/plugin/core/LocalPluginConfig.java | 18 + .../iotkit/plugin/core/LocalPluginScript.java | 50 ++ .../cc/iotkit/plugin/core/thing/IDevice.java | 49 ++ .../plugin/core/thing/IThingService.java | 49 ++ .../core/thing/actions/AbstractAction.java | 31 + .../core/thing/actions/ActionResult.java | 29 + .../plugin/core/thing/actions/ActionType.java | 39 + .../core/thing/actions/DeviceState.java | 21 + .../plugin/core/thing/actions/EventLevel.java | 24 + .../core/thing/actions/IDeviceAction.java | 79 ++ .../core/thing/actions/down/DeviceConfig.java | 40 ++ .../core/thing/actions/down/PropertyGet.java | 35 + .../core/thing/actions/down/PropertySet.java | 35 + .../thing/actions/down/ServiceInvoke.java | 45 ++ .../core/thing/actions/up/DeviceRegister.java | 36 + .../thing/actions/up/DeviceStateChange.java | 29 + .../core/thing/actions/up/DeviceTopology.java | 33 + .../core/thing/actions/up/EventReport.java | 44 ++ .../core/thing/actions/up/PropertyReport.java | 33 + .../core/thing/actions/up/ServiceReply.java | 48 ++ .../plugin/core/thing/model/ThingDevice.java | 40 ++ .../plugin/core/thing/model/ThingProduct.java | 29 + iot-script-engine/pom.xml | 2 +- iot-spring-brick/pom.xml | 98 +++ .../spring-brick-bootstrap/pom.xml | 63 ++ .../bootstrap/AutowiredTypeDefinerConfig.java | 58 ++ .../bootstrap/AutowiredTypeResolver.java | 47 ++ .../bootstrap/ConfigurePluginEnvironment.java | 150 ++++ .../DefaultAutowiredTypeResolver.java | 76 ++ .../bootstrap/DefaultSpringPluginHook.java | 120 ++++ .../EmptyMainApplicationContext.java | 106 +++ .../bootstrap/EmptySpringBeanFactory.java | 185 +++++ .../bootstrap/PluginApplicationContext.java | 63 ++ .../bootstrap/PluginContextHolder.java | 122 ++++ .../PluginDisableAutoConfiguration.java | 146 ++++ .../bootstrap/PluginListableBeanFactory.java | 364 ++++++++++ .../bootstrap/PluginOneselfInteractive.java | 107 +++ .../PluginOneselfSpringApplication.java | 86 +++ .../bootstrap/PluginSpringApplication.java | 126 ++++ .../PluginWebApplicationContext.java | 95 +++ ...lveHttpMessageConvertersConfiguration.java | 42 ++ .../bootstrap/SpringPluginBootstrap.java | 122 ++++ .../SpringPluginBootstrapBinder.java | 28 + .../bootstrap/annotation/AutowiredType.java | 48 ++ .../annotation/DisablePluginWeb.java | 33 + .../bootstrap/annotation/OneselfConfig.java | 40 ++ .../annotation/ResolveClassLoader.java | 32 + .../CoexistAllowAutoConfiguration.java | 60 ++ .../CoexistResolveClassLoaderAspect.java | 53 ++ .../bootstrap/launcher/BootstrapLauncher.java | 38 + .../launcher/BootstrapLauncherFactory.java | 37 + .../launcher/CoexistBootstrapLauncher.java | 111 +++ .../DefaultBootstrapLauncherFactory.java | 55 ++ .../launcher/IsolationBootstrapLauncher.java | 59 ++ .../launcher/OneselfBootstrapLauncher.java | 47 ++ .../PluginApplicationWebEventListener.java | 83 +++ .../ComposeSpringPluginProcessor.java | 202 ++++++ .../processor/DefaultProcessorContext.java | 150 ++++ .../processor/ExtractBeanProcessor.java | 63 ++ .../processor/FrameDefineBeanProcessor.java | 50 ++ .../processor/InvokeOtherPluginProcessor.java | 128 ++++ .../bootstrap/processor/ProcessorContext.java | 148 ++++ .../processor/ProcessorException.java | 47 ++ .../processor/SpringPluginProcessor.java | 93 +++ .../PluginInterceptorRegister.java | 35 + .../PluginInterceptorRegistration.java | 174 +++++ .../PluginInterceptorRegistry.java | 114 +++ .../processor/invoke/InvokeBeanFactory.java | 68 ++ .../processor/invoke/InvokeProxyHandler.java | 212 ++++++ .../ConfigureMainPluginEnvironment.java | 109 +++ .../processor/oneself/OneselfProcessor.java | 54 ++ .../PluginClassPathBeanDefinitionScanner.java | 42 ++ .../web/PluginControllerProcessor.java | 266 +++++++ .../web/PluginInterceptorsProcessor.java | 164 +++++ .../web/PluginStaticResourceProcessor.java | 74 ++ .../realize/AutowiredTypeDefiner.java | 95 +++ .../realize/PluginCloseListener.java | 53 ++ .../bootstrap/realize/StopValidator.java | 95 +++ .../bootstrap/utils/AnnotationUtils.java | 64 ++ .../bootstrap/utils/DestroyUtils.java | 103 +++ .../bootstrap/utils/ProcessorUtils.java | 43 ++ .../bootstrap/utils/SpringBeanUtils.java | 106 +++ .../main/resources/META-INF/spring.factories | 7 + iot-spring-brick/spring-brick-common/pom.xml | 17 + .../common/AbstractDependencyPlugin.java | 108 +++ .../com/gitee/starblues/common/Constants.java | 52 ++ .../starblues/common/DependencyPlugin.java | 49 ++ .../gitee/starblues/common/ManifestKey.java | 120 ++++ .../starblues/common/PackageStructure.java | 91 +++ .../gitee/starblues/common/PackageType.java | 39 + .../starblues/common/PluginDescriptorKey.java | 50 ++ .../common/PluginDisableAutoConfig.java | 25 + .../common/cipher/AbstractPluginCipher.java | 58 ++ .../common/cipher/AesPluginCipher.java | 83 +++ .../starblues/common/cipher/PluginCipher.java | 45 ++ .../common/cipher/RsaPluginCipher.java | 107 +++ .../com/gitee/starblues/utils/Assert.java | 89 +++ .../utils/CompareClassTypeUtils.java | 101 +++ .../gitee/starblues/utils/FieldFilter.java | 40 ++ .../com/gitee/starblues/utils/FilesUtils.java | 295 ++++++++ .../gitee/starblues/utils/MapValueGetter.java | 110 +++ .../gitee/starblues/utils/ObjectUtils.java | 485 +++++++++++++ .../starblues/utils/ObjectValueUtils.java | 91 +++ .../starblues/utils/PropertiesUtils.java | 69 ++ .../starblues/utils/ReflectionUtils.java | 248 +++++++ .../gitee/starblues/utils/ResourceUtils.java | 254 +++++++ .../gitee/starblues/utils/StringUtils.java | 79 ++ .../com/gitee/starblues/utils/UrlUtils.java | 139 ++++ iot-spring-brick/spring-brick-loader/pom.xml | 16 + .../starblues/loader/DevelopmentMode.java | 38 + .../starblues/loader/LoaderConstant.java | 53 ++ .../loader/PluginResourceStorage.java | 184 +++++ .../starblues/loader/archive/Archive.java | 145 ++++ .../loader/archive/ExplodedArchive.java | 338 +++++++++ .../loader/archive/JarFileArchive.java | 303 ++++++++ .../classloader/ClassLoaderTranslator.java | 127 ++++ .../classloader/GeneralUrlClassLoader.java | 111 +++ .../classloader/GenericClassLoader.java | 340 +++++++++ .../classloader/filter/ExcludeResource.java | 36 + .../classloader/filter/IncludeResource.java | 36 + .../loader/classloader/resource/Resource.java | 63 ++ .../resource/ResourceByteGetter.java | 35 + .../classloader/resource/cache/Cache.java | 92 +++ .../cache/CacheExpirationTrigger.java | 63 ++ .../resource/cache/CacheLinkedHashMap.java | 54 ++ .../cache/DefaultCacheExpirationTrigger.java | 112 +++ .../classloader/resource/cache/Entity.java | 51 ++ .../resource/cache/LRUMapCache.java | 227 ++++++ .../resource/cache/LRUMultiMapListCache.java | 289 ++++++++ .../cache/LRUMultiMapUnifiedListCache.java | 288 ++++++++ .../resource/cache/MultiCache.java | 49 ++ .../loader/AbstractResourceLoader.java | 98 +++ .../loader/BaseURLResourceLoader.java | 36 + .../resource/loader/CacheResource.java | 71 ++ .../resource/loader/ClassPathLoader.java | 102 +++ .../resource/loader/DefaultResource.java | 75 ++ .../loader/DefaultResourceLoaderFactory.java | 137 ++++ .../resource/loader/JarResourceLoader.java | 106 +++ .../loader/MainJarResourceLoader.java | 39 + .../resource/loader/ResourceLoader.java | 45 ++ .../loader/ResourceLoaderFactory.java | 108 +++ .../storage/AbstractResourceStorage.java | 252 +++++++ .../storage/CacheFastResourceStorage.java | 142 ++++ .../CacheLowMemoryResourceStorage.java | 149 ++++ .../CachePerpetualResourceStorage.java | 105 +++ .../storage/EmptyResourceStorage.java | 79 ++ .../resource/storage/ResourceStorage.java | 90 +++ .../starblues/loader/jar/AbstractJarFile.java | 92 +++ .../starblues/loader/jar/AsciiBytes.java | 254 +++++++ .../com/gitee/starblues/loader/jar/Bytes.java | 39 + .../loader/jar/CentralDirectoryEndRecord.java | 250 +++++++ .../jar/CentralDirectoryFileHeader.java | 191 +++++ .../loader/jar/CentralDirectoryParser.java | 100 +++ .../loader/jar/CentralDirectoryVisitor.java | 46 ++ .../starblues/loader/jar/FileHeader.java | 66 ++ .../gitee/starblues/loader/jar/Handler.java | 434 +++++++++++ .../gitee/starblues/loader/jar/JarEntry.java | 122 ++++ .../loader/jar/JarEntryCertification.java | 60 ++ .../starblues/loader/jar/JarEntryFilter.java | 37 + .../gitee/starblues/loader/jar/JarFile.java | 453 ++++++++++++ .../starblues/loader/jar/JarFileEntries.java | 412 +++++++++++ .../starblues/loader/jar/JarFileWrapper.java | 177 +++++ .../loader/jar/JarURLConnection.java | 395 ++++++++++ .../loader/jar/RandomAccessData.java | 77 ++ .../loader/jar/RandomAccessDataFile.java | 253 +++++++ .../starblues/loader/jar/StringSequence.java | 159 +++++ .../loader/jar/ZipInflaterInputStream.java | 89 +++ .../loader/launcher/AbstractLauncher.java | 58 ++ .../loader/launcher/AbstractMainLauncher.java | 92 +++ .../loader/launcher/DevLauncher.java | 50 ++ .../launcher/DevelopmentModeSetting.java | 72 ++ .../starblues/loader/launcher/Launcher.java | 38 + .../loader/launcher/LauncherContext.java | 53 ++ .../loader/launcher/ProdLauncher.java | 128 ++++ .../loader/launcher/SpringBootstrap.java | 47 ++ .../loader/launcher/SpringMainBootstrap.java | 92 +++ .../launcher/SpringMainProdBootstrap.java | 40 ++ .../launcher/classpath/ClasspathResource.java | 39 + .../classpath/FastJarClasspathResource.java | 74 ++ .../classpath/JarOutClasspathResource.java | 118 +++ .../launcher/coexist/CoexistBaseLauncher.java | 57 ++ .../coexist/CoexistFastJarLauncher.java | 61 ++ .../coexist/CoexistJarOuterLauncher.java | 70 ++ .../isolation/IsolationBaseLauncher.java | 98 +++ .../isolation/IsolationFastJarLauncher.java | 80 +++ .../isolation/IsolationJarOuterLauncher.java | 80 +++ .../ResourceLoaderFactoryGetter.java | 89 +++ .../launcher/runner/MainMethodRunner.java | 36 + .../loader/launcher/runner/MethodRunner.java | 116 +++ .../gitee/starblues/loader/utils/Assert.java | 89 +++ .../loader/utils/CompareClassTypeUtils.java | 101 +++ .../starblues/loader/utils/FilesUtils.java | 128 ++++ .../gitee/starblues/loader/utils/IOUtils.java | 81 +++ .../starblues/loader/utils/ObjectUtils.java | 81 +++ .../gitee/starblues/loader/utils/Release.java | 18 + .../starblues/loader/utils/ResourceUtils.java | 229 ++++++ .../spring-brick-maven-packager/pom.xml | 90 +++ .../pack/AbstractDependencyFilterMojo.java | 108 +++ .../plugin/pack/AbstractPackagerMojo.java | 93 +++ .../plugin/pack/BasicRepackager.java | 380 ++++++++++ .../gitee/starblues/plugin/pack/Constant.java | 85 +++ .../starblues/plugin/pack/Dependency.java | 38 + .../plugin/pack/DependencyPlugin.java | 73 ++ .../plugin/pack/LoadMainResourcePattern.java | 38 + .../starblues/plugin/pack/LoadToMain.java | 35 + .../starblues/plugin/pack/PluginInfo.java | 92 +++ .../starblues/plugin/pack/RepackageMojo.java | 133 ++++ .../starblues/plugin/pack/Repackager.java | 39 + .../starblues/plugin/pack/dev/Dependency.java | 41 ++ .../starblues/plugin/pack/dev/DevConfig.java | 44 ++ .../plugin/pack/dev/DevRepackager.java | 102 +++ .../plugin/pack/encrypt/AesConfig.java | 33 + .../plugin/pack/encrypt/AesEncryptPlugin.java | 43 ++ .../plugin/pack/encrypt/EncryptConfig.java | 41 ++ .../plugin/pack/encrypt/EncryptPlugin.java | 40 ++ .../pack/encrypt/EncryptPluginFactory.java | 50 ++ .../plugin/pack/encrypt/RsaConfig.java | 36 + .../plugin/pack/encrypt/RsaEncryptPlugin.java | 57 ++ .../plugin/pack/filter/DependencyFilter.java | 75 ++ .../starblues/plugin/pack/filter/Exclude.java | 35 + .../plugin/pack/filter/ExcludeFilter.java | 51 ++ .../pack/filter/FilterableDependency.java | 38 + .../starblues/plugin/pack/filter/Include.java | 27 + .../plugin/pack/filter/IncludeFilter.java | 46 ++ .../plugin/pack/main/JarNestPackager.java | 138 ++++ .../plugin/pack/main/JarOuterPackager.java | 146 ++++ .../plugin/pack/main/MainConfig.java | 68 ++ .../plugin/pack/main/MainRepackager.java | 136 ++++ .../plugin/pack/prod/DirProdRepackager.java | 156 ++++ .../pack/prod/JarNestedProdRepackager.java | 77 ++ .../pack/prod/JarOuterProdRepackager.java | 56 ++ .../plugin/pack/prod/ProdConfig.java | 59 ++ .../plugin/pack/prod/ProdRepackager.java | 95 +++ .../pack/prod/ZipOuterProdRepackager.java | 128 ++++ .../plugin/pack/prod/ZipProdRepackager.java | 197 +++++ .../plugin/pack/utils/CommonUtils.java | 100 +++ .../plugin/pack/utils/PackageJar.java | 58 ++ .../plugin/pack/utils/PackageZip.java | 249 +++++++ .../plugin-help.xml | 168 +++++ .../main/resources/META-INF/maven/plugin.xml | 168 +++++ iot-spring-brick/spring-brick/pom.xml | 89 +++ .../gitee/starblues/annotation/Caller.java | 60 ++ .../gitee/starblues/annotation/Extract.java | 68 ++ .../gitee/starblues/annotation/Supplier.java | 58 ++ .../core/DefaultPluginInsideInfo.java | 143 ++++ .../starblues/core/DefaultPluginManager.java | 674 ++++++++++++++++++ .../core/DefaultRealizeProvider.java | 115 +++ .../gitee/starblues/core/PluginCloseType.java | 27 + .../starblues/core/PluginExtensionInfo.java | 21 + .../com/gitee/starblues/core/PluginInfo.java | 91 +++ .../gitee/starblues/core/PluginInfoFace.java | 99 +++ .../starblues/core/PluginInsideInfo.java | 75 ++ .../starblues/core/PluginLauncherManager.java | 169 +++++ .../gitee/starblues/core/PluginManager.java | 144 ++++ .../com/gitee/starblues/core/PluginState.java | 88 +++ .../gitee/starblues/core/RealizeProvider.java | 66 ++ .../com/gitee/starblues/core/RuntimeMode.java | 60 ++ .../checker/ComposePluginBasicChecker.java | 75 ++ .../checker/ComposePluginLauncherChecker.java | 65 ++ .../checker/DefaultPluginBasicChecker.java | 75 ++ .../checker/DefaultPluginLauncherChecker.java | 99 +++ .../DependencyPluginLauncherChecker.java | 126 ++++ .../core/checker/PluginBasicChecker.java | 47 ++ .../core/checker/PluginLauncherChecker.java | 46 ++ .../classloader/CacheMainResourceMatcher.java | 55 ++ .../ComposeMainResourceMatcher.java | 73 ++ .../DefaultMainResourceMatcher.java | 82 +++ .../EmptyMainResourcePatternDefiner.java | 39 + .../core/classloader/MainResourceMatcher.java | 35 + .../MainResourcePatternDefiner.java | 42 ++ .../NestedPluginJarResourceLoader.java | 122 ++++ .../core/classloader/PluginClassLoader.java | 119 ++++ .../PluginGeneralUrlClassLoader.java | 52 ++ .../PluginResourceLoaderFactory.java | 40 ++ .../PluginResourceLoaderFactoryProxy.java | 187 +++++ .../ProhibitMainResourceMatcher.java | 33 + .../AbstractPluginDescriptorLoader.java | 247 +++++++ .../descriptor/ComposeDescriptorLoader.java | 95 +++ .../descriptor/DefaultDependencyPlugin.java | 67 ++ .../DefaultInsidePluginDescriptor.java | 136 ++++ .../descriptor/DefaultPluginDescriptor.java | 146 ++++ .../descriptor/DevPluginDescriptorLoader.java | 74 ++ .../descriptor/InsidePluginDescriptor.java | 108 +++ .../core/descriptor/PluginDescriptor.java | 93 +++ .../descriptor/PluginDescriptorLoader.java | 43 ++ .../core/descriptor/PluginLibInfo.java | 47 ++ .../descriptor/PluginResourcesConfig.java | 107 +++ .../starblues/core/descriptor/PluginType.java | 105 +++ .../ProdDirPluginDescriptorLoader.java | 131 ++++ .../ProdPackagePluginDescriptorLoader.java | 136 ++++ .../ProdPluginDescriptorLoader.java | 65 ++ .../DefaultPluginDescriptorDecrypt.java | 134 ++++ .../decrypt/EmptyPluginDescriptorDecrypt.java | 33 + .../decrypt/PluginDescriptorDecrypt.java | 42 ++ .../exception/PluginDecryptException.java | 61 ++ .../exception/PluginDisabledException.java | 57 ++ .../core/exception/PluginException.java | 71 ++ .../PluginProhibitStopException.java | 33 + .../JavaMainResourcePatternDefiner.java | 84 +++ .../plugin/AbstractPluginLauncher.java | 62 ++ .../BasicMainResourcePatternDefiner.java | 51 ++ .../launcher/plugin/CacheRegistryInfo.java | 68 ++ .../DefaultMainResourcePatternDefiner.java | 170 +++++ .../plugin/DefaultPluginInteractive.java | 87 +++ .../plugin/PluginCoexistLauncher.java | 43 ++ .../launcher/plugin/PluginInteractive.java | 70 ++ .../plugin/PluginIsolationLauncher.java | 114 +++ .../PluginMainResourcePatternDefiner.java | 63 ++ .../launcher/plugin/PluginMethodRunner.java | 96 +++ .../core/launcher/plugin/RegistryInfo.java | 65 ++ .../plugin/SpringPluginHookWrapper.java | 87 +++ .../involved/DefaultPluginLaunchInvolved.java | 95 +++ .../PluginApplicationContextGetter.java | 58 ++ .../plugin/involved/PluginLaunchInvolved.java | 86 +++ .../involved/PluginLaunchInvolvedFactory.java | 116 +++ .../core/scanner/BasePluginScanner.java | 81 +++ .../core/scanner/ComposePathResolve.java | 64 ++ .../core/scanner/DevPathResolve.java | 58 ++ .../starblues/core/scanner/PathResolve.java | 37 + .../starblues/core/scanner/PluginScanner.java | 38 + .../core/scanner/ProdPathResolve.java | 79 ++ .../core/version/SemverVersionInspector.java | 58 ++ .../core/version/VersionInspector.java | 46 ++ .../AutoIntegrationConfiguration.java | 269 +++++++ .../DefaultIntegrationConfiguration.java | 131 ++++ .../integration/ExtendPointConfiguration.java | 100 +++ .../ExtendPointWebConfiguration.java | 63 ++ .../integration/IntegrationConfiguration.java | 200 ++++++ .../integration/SpringBootPluginStarter.java | 36 + .../AbstractPluginApplication.java | 54 ++ .../application/AutoPluginApplication.java | 88 +++ .../application/DefaultPluginApplication.java | 171 +++++ .../application/PluginApplication.java | 50 ++ .../decrypt/DecryptConfiguration.java | 61 ++ .../decrypt/DecryptPluginConfiguration.java | 43 ++ .../listener/DefaultInitializerListener.java | 52 ++ .../DefaultPluginListenerFactory.java | 142 ++++ .../listener/PluginInitializerListener.java | 45 ++ .../PluginInitializerListenerFactory.java | 93 +++ .../integration/listener/PluginListener.java | 86 +++ .../listener/PluginListenerFactory.java | 43 ++ .../operator/DefaultPluginOperator.java | 461 ++++++++++++ .../operator/EmptyPluginOperator.java | 94 +++ .../integration/operator/PluginOperator.java | 175 +++++ .../operator/PluginOperatorWrapper.java | 183 +++++ .../upload/UploadByInputStreamParam.java | 52 ++ .../upload/UploadByMultipartFileParam.java | 38 + .../operator/upload/UploadParam.java | 99 +++ .../integration/user/BeanWrapper.java | 50 ++ .../integration/user/DefaultPluginUser.java | 159 +++++ .../integration/user/PluginUser.java | 98 +++ .../starblues/spring/ApplicationContext.java | 39 + .../spring/ApplicationContextProxy.java | 42 ++ .../CacheJdkSameTypeParamProxyFactory.java | 59 ++ .../spring/GenericApplicationContext.java | 66 ++ .../spring/JdkSameTypeParamProxyFactory.java | 51 ++ .../spring/MainApplicationContext.java | 97 +++ .../spring/MainApplicationContextProxy.java | 162 +++++ .../gitee/starblues/spring/ProxyFactory.java | 34 + .../ResolvePluginThreadClassLoader.java | 71 ++ .../starblues/spring/SpringBeanFactory.java | 29 + .../starblues/spring/SpringPluginHook.java | 57 ++ .../com/gitee/starblues/spring/WebConfig.java | 34 + .../environment/EmptyEnvironmentProvider.java | 74 ++ .../environment/EnvironmentProvider.java | 92 +++ .../MainSpringBootEnvironmentProvider.java | 108 +++ .../environment/MapEnvironmentProvider.java | 119 ++++ .../spring/extract/DefaultExtractFactory.java | 72 ++ .../extract/DefaultOpExtractFactory.java | 213 ++++++ .../spring/extract/ExtractCoordinate.java | 120 ++++ .../spring/extract/ExtractFactory.java | 97 +++ .../spring/extract/OpExtractFactory.java | 45 ++ .../invoke/DefaultInvokeSupperCache.java | 81 +++ .../spring/invoke/InvokeSupperCache.java | 57 ++ .../starblues/spring/invoke/SupperCache.java | 50 ++ .../starblues/spring/web/PluginResource.java | 184 +++++ .../web/PluginStaticResourceConfig.java | 84 +++ .../web/PluginStaticResourceResolver.java | 407 +++++++++++ .../PluginStaticResourceWebMvcConfigurer.java | 53 ++ .../starblues/utils/AnnotationsUtils.java | 70 ++ .../com/gitee/starblues/utils/ClassUtils.java | 141 ++++ .../com/gitee/starblues/utils/LogUtils.java | 41 ++ .../com/gitee/starblues/utils/MsgUtils.java | 52 ++ .../java/com/gitee/starblues/utils/Order.java | 34 + .../gitee/starblues/utils/OrderPriority.java | 119 ++++ .../com/gitee/starblues/utils/OrderUtils.java | 68 ++ .../starblues/utils/PluginConfigUtils.java | 134 ++++ .../starblues/utils/PluginFileUtils.java | 275 +++++++ .../com/gitee/starblues/utils/ScanUtils.java | 150 ++++ .../utils/SpringBeanCustomUtils.java | 135 ++++ .../starblues/utils/SpringBeanUtils.java | 128 ++++ .../spring-configuration-metadata.json | 157 ++++ .../main/resources/META-INF/spring.factories | 2 + pom.xml | 10 +- 413 files changed, 38766 insertions(+), 17 deletions(-) create mode 100644 iot-plugin-core/pom.xml create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPlugin.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginConfig.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginScript.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginConfig.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginScript.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IDevice.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IThingService.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/AbstractAction.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionResult.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionType.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/DeviceState.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/EventLevel.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/IDeviceAction.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/DeviceConfig.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertyGet.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertySet.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/ServiceInvoke.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceRegister.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceStateChange.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceTopology.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/EventReport.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/PropertyReport.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/ServiceReply.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingDevice.java create mode 100755 iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingProduct.java create mode 100644 iot-spring-brick/pom.xml create mode 100755 iot-spring-brick/spring-brick-bootstrap/pom.xml create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeDefinerConfig.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeResolver.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultAutowiredTypeResolver.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginContextHolder.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginDisableAutoConfiguration.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfSpringApplication.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginWebApplicationContext.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ResolveHttpMessageConvertersConfiguration.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrapBinder.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/AutowiredType.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/ResolveClassLoader.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistAllowAutoConfiguration.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistResolveClassLoaderAspect.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncher.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncherFactory.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/CoexistBootstrapLauncher.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/DefaultBootstrapLauncherFactory.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/IsolationBootstrapLauncher.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/OneselfBootstrapLauncher.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/listener/PluginApplicationWebEventListener.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/ConfigureMainPluginEnvironment.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/OneselfProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/AutowiredTypeDefiner.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java create mode 100755 iot-spring-brick/spring-brick-bootstrap/src/main/resources/META-INF/spring.factories create mode 100755 iot-spring-brick/spring-brick-common/pom.xml create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDisableAutoConfig.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AbstractPluginCipher.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AesPluginCipher.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/PluginCipher.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/RsaPluginCipher.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/MapValueGetter.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectValueUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java create mode 100755 iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java create mode 100755 iot-spring-brick/spring-brick-loader/pom.xml create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/DevelopmentMode.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/LoaderConstant.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/ClassLoaderTranslator.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GeneralUrlClassLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Cache.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheExpirationTrigger.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheLinkedHashMap.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/DefaultCacheExpirationTrigger.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Entity.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMapCache.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapListCache.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapUnifiedListCache.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/MultiCache.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/BaseURLResourceLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/CacheResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/MainJarResourceLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/AbstractResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheFastResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheLowMemoryResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CachePerpetualResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/EmptyResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractMainLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevelopmentModeSetting.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/LauncherContext.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ProdLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/ClasspathResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/FastJarClasspathResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/JarOutClasspathResource.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistBaseLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistFastJarLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistJarOuterLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationBaseLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationFastJarLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationJarOuterLauncher.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/ResourceLoaderFactoryGetter.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/FilesUtils.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Release.java create mode 100755 iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/pom.xml create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesConfig.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesEncryptPlugin.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptConfig.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPlugin.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPluginFactory.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaConfig.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaEncryptPlugin.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml create mode 100755 iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml create mode 100755 iot-spring-brick/spring-brick/pom.xml create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginCloseType.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginExtensionInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ComposeMainResourceMatcher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginGeneralUrlClassLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactoryProxy.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/DefaultPluginDescriptorDecrypt.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/EmptyPluginDescriptorDecrypt.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/PluginDescriptorDecrypt.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDecryptException.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/AbstractPluginLauncher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultMainResourcePatternDefiner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginCoexistLauncher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginIsolationLauncher.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptConfiguration.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptPluginConfiguration.java create mode 100644 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java create mode 100644 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java create mode 100644 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java create mode 100644 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java create mode 100644 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java create mode 100644 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/EmptyPluginOperator.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ResolvePluginThreadClassLoader.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EmptyEnvironmentProvider.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EnvironmentProvider.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MainSpringBootEnvironmentProvider.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MapEnvironmentProvider.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java create mode 100755 iot-spring-brick/spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json create mode 100755 iot-spring-brick/spring-brick/src/main/resources/META-INF/spring.factories diff --git a/iot-common-core/pom.xml b/iot-common-core/pom.xml index 7380466..3a2e36e 100755 --- a/iot-common-core/pom.xml +++ b/iot-common-core/pom.xml @@ -6,7 +6,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 iot-common-core diff --git a/iot-common-doc/pom.xml b/iot-common-doc/pom.xml index d8c6a96..18c14ca 100755 --- a/iot-common-doc/pom.xml +++ b/iot-common-doc/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-excel/pom.xml b/iot-common-excel/pom.xml index 745d62e..ede96fc 100755 --- a/iot-common-excel/pom.xml +++ b/iot-common-excel/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 @@ -30,6 +30,11 @@ easyexcel + + org.apache.xmlbeans + xmlbeans + + diff --git a/iot-common-log/pom.xml b/iot-common-log/pom.xml index 6692feb..75538e4 100755 --- a/iot-common-log/pom.xml +++ b/iot-common-log/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-oss/pom.xml b/iot-common-oss/pom.xml index 2ca2521..b089b53 100755 --- a/iot-common-oss/pom.xml +++ b/iot-common-oss/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-redis/pom.xml b/iot-common-redis/pom.xml index 897c50b..093db94 100755 --- a/iot-common-redis/pom.xml +++ b/iot-common-redis/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-satoken/pom.xml b/iot-common-satoken/pom.xml index 2f9e6f2..bc6dcf3 100755 --- a/iot-common-satoken/pom.xml +++ b/iot-common-satoken/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-tenant/pom.xml b/iot-common-tenant/pom.xml index 85ee084..6bfda65 100755 --- a/iot-common-tenant/pom.xml +++ b/iot-common-tenant/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-thing/pom.xml b/iot-common-thing/pom.xml index cd64446..003916a 100755 --- a/iot-common-thing/pom.xml +++ b/iot-common-thing/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-web/pom.xml b/iot-common-web/pom.xml index e141faa..8ce49b0 100755 --- a/iot-common-web/pom.xml +++ b/iot-common-web/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-common-websocket/pom.xml b/iot-common-websocket/pom.xml index ec7dc71..afb83c7 100755 --- a/iot-common-websocket/pom.xml +++ b/iot-common-websocket/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-message-bus/iot-message-core/pom.xml b/iot-message-bus/iot-message-core/pom.xml index abf505b..19f53cd 100755 --- a/iot-message-bus/iot-message-core/pom.xml +++ b/iot-message-bus/iot-message-core/pom.xml @@ -5,7 +5,7 @@ iot-message-bus cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-message-bus/iot-message-event-bus/pom.xml b/iot-message-bus/iot-message-event-bus/pom.xml index 1a34eb4..bb80bd9 100755 --- a/iot-message-bus/iot-message-event-bus/pom.xml +++ b/iot-message-bus/iot-message-event-bus/pom.xml @@ -5,7 +5,7 @@ iot-message-bus cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-message-bus/iot-message-rocketmq/pom.xml b/iot-message-bus/iot-message-rocketmq/pom.xml index 213dffc..3db7f54 100755 --- a/iot-message-bus/iot-message-rocketmq/pom.xml +++ b/iot-message-bus/iot-message-rocketmq/pom.xml @@ -5,7 +5,7 @@ iot-message-bus cc.iotkit - 1.0.1 + 1.0.2 4.0.0 diff --git a/iot-message-bus/pom.xml b/iot-message-bus/pom.xml index 9fd5c82..a478c3e 100755 --- a/iot-message-bus/pom.xml +++ b/iot-message-bus/pom.xml @@ -5,7 +5,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 4.0.0 pom diff --git a/iot-plugin-core/pom.xml b/iot-plugin-core/pom.xml new file mode 100644 index 0000000..0070567 --- /dev/null +++ b/iot-plugin-core/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + cc.iotkit + iot-iita-core + 1.0.2 + + + iot-plugin-core + + + 11 + 11 + UTF-8 + + 3.1.4 + + + + + + cc.iotkit + iot-common-core + + + + cc.iotkit + iot-script-engine + ${project.version} + + + + cc.iotkit + iot-message-core + + + + + + org.projectlombok + lombok + + + + com.gitee.starblues + spring-brick + ${spring-brick.version} + + + + + \ No newline at end of file diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPlugin.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPlugin.java new file mode 100755 index 0000000..6dd24a7 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPlugin.java @@ -0,0 +1,22 @@ +package cc.iotkit.plugin.core; + + +import java.util.Map; + +/** + * 插件接口 + * + * @author sjg + */ +public interface IPlugin { + + /** + * 获取设备连接信息,如连接mqtt的ip、端口、账号、密码。。。 + * + * @param pk 产品key + * @param dn 设备dn + * @return 连接配置项 + */ + Map getLinkInfo(String pk, String dn); + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginConfig.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginConfig.java new file mode 100755 index 0000000..77c964f --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginConfig.java @@ -0,0 +1,21 @@ +package cc.iotkit.plugin.core; + + +import java.util.Map; + +/** + * 插件配置接口 + * + * @author sjg + */ +public interface IPluginConfig { + + /** + * 获取系统中配置的插件配置项 + * + * @param pluginId 插件id + * @return config + */ + Map getConfig(String pluginId); + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginScript.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginScript.java new file mode 100755 index 0000000..3859bfd --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/IPluginScript.java @@ -0,0 +1,27 @@ +package cc.iotkit.plugin.core; + +import cc.iotkit.script.IScriptEngine; + +/** + * 插件宿主接口 + * + * @author sjg + */ +public interface IPluginScript { + + /** + * 获取插件对应的脚本引擎 + * + * @param pluginId 插件id + * @return IScriptEngine + */ + IScriptEngine getScriptEngine(String pluginId); + + /** + * 重新加载脚本 + * + * @param pluginId 插件id + */ + void reloadScript(String pluginId); + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginConfig.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginConfig.java new file mode 100755 index 0000000..a8703a3 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginConfig.java @@ -0,0 +1,18 @@ +package cc.iotkit.plugin.core; + + +import java.util.HashMap; +import java.util.Map; + +/** + * 本地独立运行的插件配置 + * + * @author sjg + */ +public class LocalPluginConfig implements IPluginConfig { + @Override + public Map getConfig(String pluginId) { + //本地的直接用程序中默认值 + return new HashMap<>(0); + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginScript.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginScript.java new file mode 100755 index 0000000..da82eb3 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/LocalPluginScript.java @@ -0,0 +1,50 @@ +package cc.iotkit.plugin.core; + +import cc.iotkit.common.utils.StringUtils; +import cc.iotkit.common.utils.file.FileUtils; +import cc.iotkit.script.IScriptEngine; +import cc.iotkit.script.ScriptEngineFactory; + +import java.net.URL; +import java.nio.charset.Charset; + +/** + * 本地独立运行的插件脚本实现 + * + * @author sjg + */ +public class LocalPluginScript implements IPluginScript { + + private IScriptEngine scriptEngine; + + public LocalPluginScript(String scriptPath) { + if (StringUtils.isBlank(scriptPath)) { + return; + } + URL resource = LocalPluginScript.class.getClassLoader().getResource(scriptPath); + if (resource == null) { + return; + } + + String script = FileUtils.readString(resource.getFile(), Charset.defaultCharset()); + initScriptEngine(script); + } + + public IScriptEngine initScriptEngine(String script) { + if (StringUtils.isBlank(script)) { + return null; + } + + scriptEngine = ScriptEngineFactory.getJsEngine(script); + return scriptEngine; + } + + @Override + public IScriptEngine getScriptEngine(String pluginId) { + return scriptEngine; + } + + @Override + public void reloadScript(String pluginId) { + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IDevice.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IDevice.java new file mode 100755 index 0000000..d97e6a6 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IDevice.java @@ -0,0 +1,49 @@ +package cc.iotkit.plugin.core.thing; + +import cc.iotkit.plugin.core.thing.actions.ActionResult; +import cc.iotkit.plugin.core.thing.actions.down.DeviceConfig; +import cc.iotkit.plugin.core.thing.actions.down.PropertyGet; +import cc.iotkit.plugin.core.thing.actions.down.PropertySet; +import cc.iotkit.plugin.core.thing.actions.down.ServiceInvoke; + + +/** + * 设备接口 + * + * @author sjg + */ +public interface IDevice { + + /** + * 执行设备配置动作 + * + * @param action 动作 + * @return result + */ + ActionResult config(DeviceConfig action); + + /** + * 执行设备属性获取动作 + * + * @param action 动作 + * @return result + */ + ActionResult propertyGet(PropertyGet action); + + /** + * 执行设备属性设置动作 + * + * @param action 动作 + * @return result + */ + ActionResult propertySet(PropertySet action); + + /** + * 执行设备服务调用动作 + * + * @param action 动作 + * @return result + */ + ActionResult serviceInvoke(ServiceInvoke action); + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IThingService.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IThingService.java new file mode 100755 index 0000000..228b2b6 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/IThingService.java @@ -0,0 +1,49 @@ +package cc.iotkit.plugin.core.thing; + +import cc.iotkit.plugin.core.thing.actions.ActionResult; +import cc.iotkit.plugin.core.thing.actions.IDeviceAction; +import cc.iotkit.plugin.core.thing.model.ThingDevice; +import cc.iotkit.plugin.core.thing.model.ThingProduct; + +import java.util.Map; + +/** + * 设备服务接口 + * + * @author sjg + */ +public interface IThingService { + + /** + * 提交设备行为 + * + * @param action IDeviceAction + * @return result + */ + ActionResult post(String pluginId, IDeviceAction action); + + /** + * 获取产品信息 + * + * @param pk 产品key + * @return Product + */ + ThingProduct getProduct(String pk); + + /** + * 获取设备信息 + * + * @param deviceName 设备dn + * @return DeviceInfo + */ + ThingDevice getDevice(String deviceName); + + /** + * 获取设备当前属性数据 + * + * @param deviceName 设备dn + * @return 当前属性 + */ + Map getProperty(String deviceName); + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/AbstractAction.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/AbstractAction.java new file mode 100755 index 0000000..dafe917 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/AbstractAction.java @@ -0,0 +1,31 @@ +package cc.iotkit.plugin.core.thing.actions; + +import lombok.Data; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * @author sjg + */ +@Data +@SuperBuilder +@ToString(callSuper = true) +public abstract class AbstractAction implements IDeviceAction { + + protected String id; + + protected ActionType type; + + protected String productKey; + + protected String deviceName; + + protected Long time; + + public AbstractAction() { + } + + public AbstractAction(ActionType type) { + this.type = type; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionResult.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionResult.java new file mode 100755 index 0000000..ec6c2d4 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionResult.java @@ -0,0 +1,29 @@ +package cc.iotkit.plugin.core.thing.actions; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 动作执行结果 + * + * @author sjg + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ActionResult { + + /** + * 状态码,0:成功,x:其它错误码 + */ + private int code; + + /** + * 失败原因 + */ + private String reason; + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionType.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionType.java new file mode 100755 index 0000000..011aa00 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/ActionType.java @@ -0,0 +1,39 @@ +package cc.iotkit.plugin.core.thing.actions; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备物行为类型 + * + * @author sjg + */ +@Getter +@AllArgsConstructor +public enum ActionType { + + //注册 + REGISTER("register"), + //设备拓扑更新 + TOPOLOGY("topology"), + //在离线状态变更 + STATE_CHANGE("state_change"), + //属性上报 + PROPERTY_REPORT("property_report"), + //事件上报 + EVENT_REPORT("event_report"), + //服务回复 + SERVICE_REPLY("service_reply"), + + //属性设置 + PROPERTY_SET("property_set"), + //属性获取 + PROPERTY_GET("property_get"), + //服务调用 + SERVICE_INVOKE("service_invoke"), + //配置 + CONFIG("config"); + + private final String type; + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/DeviceState.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/DeviceState.java new file mode 100755 index 0000000..1e107a9 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/DeviceState.java @@ -0,0 +1,21 @@ +package cc.iotkit.plugin.core.thing.actions; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备状态 + * @author sjg + */ +@Getter +@AllArgsConstructor +public enum DeviceState { + + //在线 + ONLINE("online"), + //离线 + OFFLINE("offline"); + + private final String state; + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/EventLevel.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/EventLevel.java new file mode 100755 index 0000000..13e004b --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/EventLevel.java @@ -0,0 +1,24 @@ +package cc.iotkit.plugin.core.thing.actions; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 事件级别 + * + * @author sjg + */ +@Getter +@AllArgsConstructor +public enum EventLevel { + + //信息 + INFO("info"), + //告警 + WARN("warn"), + //错误 + ERROR("error"); + + private final String type; + +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/IDeviceAction.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/IDeviceAction.java new file mode 100755 index 0000000..35219b4 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/IDeviceAction.java @@ -0,0 +1,79 @@ +package cc.iotkit.plugin.core.thing.actions; + +/** + * 设备行为 + * + * @author sjg + */ +public interface IDeviceAction { + + /** + * 获取唯一标识id + * + * @return id + */ + String getId(); + + /** + * 设置id + * + * @param id id + */ + void setId(String id); + + /** + * 获取类型 + * + * @return ActionType + */ + ActionType getType(); + + /** + * 设备类型 + * + * @param type type + */ + void setType(ActionType type); + + /** + * 获取产品key + * + * @return ProductKey + */ + String getProductKey(); + + /** + * 设置产品key + * + * @param productKey pk + */ + void setProductKey(String productKey); + + /** + * 获取设备DN + * + * @return DN + */ + String getDeviceName(); + + /** + * 设置设备DN + * + * @param deviceName dn + */ + void setDeviceName(String deviceName); + + /** + * 获取时间 + * + * @return timespan + */ + Long getTime(); + + /** + * 设置时间 + * + * @param time timestamp + */ + void setTime(Long time); +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/DeviceConfig.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/DeviceConfig.java new file mode 100755 index 0000000..5af6966 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/DeviceConfig.java @@ -0,0 +1,40 @@ +package cc.iotkit.plugin.core.thing.actions.down; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +/** + * 设备配置 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +public class DeviceConfig extends AbstractAction { + + /** + * 配置模块 + */ + private String module; + + /** + * 配置信息 + */ + private Map config; + + @Override + public ActionType getType() { + return ActionType.CONFIG; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertyGet.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertyGet.java new file mode 100755 index 0000000..080ee24 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertyGet.java @@ -0,0 +1,35 @@ +package cc.iotkit.plugin.core.thing.actions.down; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * 属性获取 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +public class PropertyGet extends AbstractAction { + + /** + * 属性列表 + */ + private List keys; + + @Override + public ActionType getType() { + return ActionType.PROPERTY_GET; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertySet.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertySet.java new file mode 100755 index 0000000..fc74efb --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/PropertySet.java @@ -0,0 +1,35 @@ +package cc.iotkit.plugin.core.thing.actions.down; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +/** + * 属性设置 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +public class PropertySet extends AbstractAction { + + /** + * 属性参数 + */ + private Map params; + + @Override + public ActionType getType() { + return ActionType.PROPERTY_SET; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/ServiceInvoke.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/ServiceInvoke.java new file mode 100755 index 0000000..f76394e --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/down/ServiceInvoke.java @@ -0,0 +1,45 @@ +package cc.iotkit.plugin.core.thing.actions.down; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +/** + * 服务调用 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +public class ServiceInvoke extends AbstractAction { + + /** + * 服务名 + */ + private String name; + + /** + * 服务参数 + */ + private Map params; + + /** + * 配置信息 + */ + private Map config; + + @Override + public ActionType getType() { + return ActionType.SERVICE_INVOKE; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceRegister.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceRegister.java new file mode 100755 index 0000000..ec1e448 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceRegister.java @@ -0,0 +1,36 @@ +package cc.iotkit.plugin.core.thing.actions.up; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * 设备注册动作 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@ToString(callSuper = true) +public class DeviceRegister extends AbstractAction { + + /** + * 型号 + */ + private String model; + + /** + * 版本号 + */ + private String version; + + @Override + public ActionType getType() { + return ActionType.REGISTER; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceStateChange.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceStateChange.java new file mode 100755 index 0000000..1757952 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceStateChange.java @@ -0,0 +1,29 @@ +package cc.iotkit.plugin.core.thing.actions.up; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import cc.iotkit.plugin.core.thing.actions.DeviceState; +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * 设备在线状态变更 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@ToString(callSuper = true) +public class DeviceStateChange extends AbstractAction { + + private DeviceState state; + + @Override + public ActionType getType() { + return ActionType.STATE_CHANGE; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceTopology.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceTopology.java new file mode 100755 index 0000000..b483bdd --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/DeviceTopology.java @@ -0,0 +1,33 @@ +package cc.iotkit.plugin.core.thing.actions.up; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * 设备拓扑更新 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@ToString(callSuper = true) +public class DeviceTopology extends AbstractAction { + + /** + * 父设备下的子设备列表 + */ + private List subDevices; + + @Override + public ActionType getType() { + return ActionType.TOPOLOGY; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/EventReport.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/EventReport.java new file mode 100755 index 0000000..60d804f --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/EventReport.java @@ -0,0 +1,44 @@ +package cc.iotkit.plugin.core.thing.actions.up; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import cc.iotkit.plugin.core.thing.actions.EventLevel; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +/** + * 事件上报 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@ToString(callSuper = true) +public class EventReport extends AbstractAction { + + /** + * 事件名 + */ + private String name; + + /** + * 事件级别 + */ + private EventLevel level; + + /** + * 事件参数 + */ + private Map params; + + @Override + public ActionType getType() { + return ActionType.EVENT_REPORT; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/PropertyReport.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/PropertyReport.java new file mode 100755 index 0000000..d06a2a1 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/PropertyReport.java @@ -0,0 +1,33 @@ +package cc.iotkit.plugin.core.thing.actions.up; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +/** + * 属性上报 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@ToString(callSuper = true) +public class PropertyReport extends AbstractAction { + + /** + * 属性参数 + */ + private Map params; + + @Override + public ActionType getType() { + return ActionType.PROPERTY_REPORT; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/ServiceReply.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/ServiceReply.java new file mode 100755 index 0000000..a186b9b --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/actions/up/ServiceReply.java @@ -0,0 +1,48 @@ +package cc.iotkit.plugin.core.thing.actions.up; + + +import cc.iotkit.plugin.core.thing.actions.AbstractAction; +import cc.iotkit.plugin.core.thing.actions.ActionType; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.Map; + +/** + * 服务回复 + * + * @author sjg + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +@SuperBuilder +@ToString(callSuper = true) +public class ServiceReply extends AbstractAction { + + /** + * 服务名 + */ + private String name; + + /** + * 回复服务id + */ + private String replyId; + + /** + * 状态码,0:成功,x:失败错误码 + */ + private int code; + + /** + * 服务回复参数 + */ + private Map params; + + @Override + public ActionType getType() { + return ActionType.SERVICE_REPLY; + } +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingDevice.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingDevice.java new file mode 100755 index 0000000..55e1353 --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingDevice.java @@ -0,0 +1,40 @@ +package cc.iotkit.plugin.core.thing.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 设备信息 + * + * @author sjg + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ThingDevice { + + private String deviceId; + + /** + * 产品key + */ + private String productKey; + + /** + * 设备dn + */ + private String deviceName; + + /** + * 设备型号 + */ + private String model; + + /** + * 设备密钥 + */ + private String secret; +} diff --git a/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingProduct.java b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingProduct.java new file mode 100755 index 0000000..902644f --- /dev/null +++ b/iot-plugin-core/src/main/java/cc/iotkit/plugin/core/thing/model/ThingProduct.java @@ -0,0 +1,29 @@ +package cc.iotkit.plugin.core.thing.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 产品信息 + * + * @author sjg + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ThingProduct { + + private String productKey; + + private String productSecret; + + private String name; + + private String category; + + private Integer nodeType; + +} diff --git a/iot-script-engine/pom.xml b/iot-script-engine/pom.xml index c2da2b0..019d7c6 100755 --- a/iot-script-engine/pom.xml +++ b/iot-script-engine/pom.xml @@ -6,7 +6,7 @@ iot-iita-core cc.iotkit - 1.0.1 + 1.0.2 iot-script-engine diff --git a/iot-spring-brick/pom.xml b/iot-spring-brick/pom.xml new file mode 100644 index 0000000..010e06d --- /dev/null +++ b/iot-spring-brick/pom.xml @@ -0,0 +1,98 @@ + + + 4.0.0 + + cc.iotkit + iot-iita-core + 1.0.2 + + + com.gitee.starblues + iot-spring-brick + pom + 3.1.4 + + + 11 + 11 + UTF-8 + + + + spring-brick-common + spring-brick-loader + spring-brick + spring-brick-bootstrap + spring-brick-maven-packager + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + https://gitee.com/starblues/springboot-plugin-framework-parent + scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git + scm:https://gitee.com/starblues/springboot-plugin-framework-parent.git + 1.0 + + + + + StarBlues + starblues@foxmail.com + https://gitee.com/starblues/ + + + + + + org.projectlombok + lombok + ${lombok.version} + provided + true + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + + + + + + + dev + + true + + + true + + + + + release + + false + + + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-bootstrap/pom.xml b/iot-spring-brick/spring-brick-bootstrap/pom.xml new file mode 100755 index 0000000..69c9549 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + + com.gitee.starblues + iot-spring-brick + 3.1.4 + + + spring-brick-bootstrap + jar + + 插件启动引导模块 + + + 4.0.1 + + + + + org.aspectj + aspectjweaver + + + + org.slf4j + slf4j-api + + + + com.gitee.starblues + spring-brick + ${project.version} + provided + true + + + + com.fasterxml.jackson.core + jackson-databind + + + + org.springframework.boot + spring-boot + ${spring-boot.version} + provided + true + + + + javax.servlet + javax.servlet-api + ${javax.servlet-api.version} + provided + true + + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeDefinerConfig.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeDefinerConfig.java new file mode 100755 index 0000000..68b9911 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeDefinerConfig.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.gitee.starblues.bootstrap.realize.AutowiredTypeDefiner; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.*; + +/** + * 配置 ClassDefiner + * + * @author starBlues + * @since 3.0.3 + * @version 3.0.3 + */ +public class AutowiredTypeDefinerConfig { + + private final Set classDefiners; + + public AutowiredTypeDefinerConfig(){ + this.classDefiners = new HashSet<>(); + } + + Set getClassDefiners(){ + return classDefiners; + } + + public AutowiredTypeDefinerConfig add(AutowiredType.Type type, String... classNamePatterns){ + if(type != null && classNamePatterns != null && classNamePatterns.length > 0){ + classDefiners.add(AutowiredTypeDefiner.ClassDefiner.config(type, classNamePatterns)); + } + return this; + } + + public AutowiredTypeDefinerConfig add(AutowiredType.Type type, Class... classes){ + if(type != null && classes != null && classes.length > 0){ + classDefiners.add(AutowiredTypeDefiner.ClassDefiner.config(type, classes)); + } + return this; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeResolver.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeResolver.java new file mode 100755 index 0000000..b4eb84f --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/AutowiredTypeResolver.java @@ -0,0 +1,47 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.realize.AutowiredTypeDefiner; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.UrlUtils; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.util.Collections; +import java.util.Set; + +/** + * 注入类型处理 + * + * @author starBlues + * @since 3.0.3 + * @version 3.1.0 + */ +public interface AutowiredTypeResolver { + + /** + * 通过 descriptor 获取注入类型 + * @param descriptor descriptor + * @return AutowiredType.Type + */ + AutowiredType.Type resolve(DependencyDescriptor descriptor); + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java new file mode 100755 index 0000000..0f53317 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ConfigurePluginEnvironment.java @@ -0,0 +1,150 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.AutoIntegrationConfiguration; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.loader.launcher.DevelopmentModeSetting; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.utils.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.LiveBeansView; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * 插件环境配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class ConfigurePluginEnvironment { + private final Logger logger = LoggerFactory.getLogger(ConfigurePluginEnvironment.class); + + private final static String PLUGIN_PROPERTY_NAME = "pluginPropertySources"; + + private final static String SPRING_CONFIG_NAME = "spring.config.name"; + private final static String SPRING_CONFIG_LOCATION = "spring.config.location"; + + private final static String SPRING_JMX_UNIQUE_NAMES = "spring.jmx.unique-names"; + private final static String SPRING_ADMIM_ENABLED = "spring.application.admin.enabled"; + private final static String SPRING_ADMIN_JMX_NAME = "spring.application.admin.jmx-name"; + private final static String SPRING_ADMIN_JMX_VALUE = "org.springframework.boot:type=Admin,name="; + + public static final String REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook"; + public static final String MBEAN_DOMAIN_PROPERTY_NAME = "spring.liveBeansView.mbeanDomain"; + + private final ProcessorContext processorContext; + private final InsidePluginDescriptor pluginDescriptor; + + public ConfigurePluginEnvironment(ProcessorContext processorContext) { + this.processorContext = Assert.isNotNull(processorContext, "processorContext 不能为空"); + this.pluginDescriptor = Assert.isNotNull(processorContext.getPluginDescriptor(), + "pluginDescriptor 不能为空"); + } + + public void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + Map env = new HashMap<>(); + String pluginId = pluginDescriptor.getPluginId(); + String configFileName = pluginDescriptor.getConfigFileName(); + if(!ObjectUtils.isEmpty(configFileName)){ + env.put(SPRING_CONFIG_NAME, PluginFileUtils.getFileName(configFileName)); + } + String configFileLocation = pluginDescriptor.getConfigFileLocation(); + if(!ObjectUtils.isEmpty(configFileLocation)){ + env.put(SPRING_CONFIG_LOCATION, getConfigFileLocation(configFileLocation)); + } + env.put(AutoIntegrationConfiguration.ENABLE_STARTER_KEY, false); + env.put(SPRING_JMX_UNIQUE_NAMES, true); + // 直接禁用插件的 spring-admin mbean + env.put(SPRING_ADMIM_ENABLED, false); + env.put(SPRING_ADMIN_JMX_NAME, SPRING_ADMIN_JMX_VALUE + pluginId); + env.put(REGISTER_SHUTDOWN_HOOK_PROPERTY, false); + env.put(MBEAN_DOMAIN_PROPERTY_NAME, pluginId); + + + try{ + // fix: https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I57965 + // 优先注册LiveBeansView对象,防止注册异常 + Method method = LiveBeansView.class.getDeclaredMethod("registerApplicationContext", ConfigurableApplicationContext.class); + method.setAccessible(true); + method.invoke(null,processorContext.getApplicationContext()); + } catch (Exception ex){ + logger.error("LiveBeansView.registerApplicationContext失败. {}", ex.getMessage(), ex); + } + if(DevelopmentModeSetting.coexist()){ + env.put(AutoIntegrationConfiguration.ENABLE_STARTER_KEY, false); + } + configProfiles(environment); + environment.getPropertySources().addFirst(new MapPropertySource(PLUGIN_PROPERTY_NAME, env)); + } + + public void logProfiles(ConfigurableEnvironment environment){ + IntegrationConfiguration configuration = processorContext.getConfiguration(); + String fromMainMsg = configuration.pluginFollowProfile() ? " from main" : " "; + String[] activeProfiles = environment.getActiveProfiles(); + if(activeProfiles.length > 0){ + logger.info("Plugin[{}] following profiles are active{}: {}", + MsgUtils.getPluginUnique(pluginDescriptor), + fromMainMsg, + StringUtils.toStrByArray(activeProfiles)); + } else { + logger.info("Plugin[{}] No active profile set, falling back to default profiles{}: {}", + MsgUtils.getPluginUnique(pluginDescriptor), + fromMainMsg, + StringUtils.toStrByArray(environment.getDefaultProfiles())); + } + } + + private void configProfiles(ConfigurableEnvironment environment){ + IntegrationConfiguration configuration = processorContext.getConfiguration(); + if(!configuration.pluginFollowProfile()){ + return; + } + MainApplicationContext mainApplicationContext = processorContext.getMainApplicationContext(); + String[] mainActiveProfiles = mainApplicationContext.getActiveProfiles(); + if(mainActiveProfiles.length > 0){ + environment.setActiveProfiles(mainActiveProfiles); + } else { + logger.info("Plugin[{}] No active profile set, falling back to default profiles: {}", + MsgUtils.getPluginUnique(pluginDescriptor), + StringUtils.toStrByArray(environment.getDefaultProfiles())); + } + } + + + private String getConfigFileLocation(String configFileLocation){ + String path = FilesUtils.resolveRelativePath(new File("").getAbsolutePath(), configFileLocation); + // 拼接最后字符斜杠 + if(path.endsWith(FilesUtils.SLASH) || path.endsWith(File.separator)){ + return path; + } else { + return path + File.separator; + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultAutowiredTypeResolver.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultAutowiredTypeResolver.java new file mode 100755 index 0000000..441c56a --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultAutowiredTypeResolver.java @@ -0,0 +1,76 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.realize.AutowiredTypeDefiner; +import com.gitee.starblues.utils.UrlUtils; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.util.Collections; +import java.util.Set; + +/** + * 默认的 AutowiredTypeResolver 实现 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class DefaultAutowiredTypeResolver implements AutowiredTypeResolver{ + + + private final Set classDefiners; + private final PathMatcher pathMatcher = new AntPathMatcher(); + + + public DefaultAutowiredTypeResolver(ProcessorContext processorContext) { + AutowiredTypeDefiner autowiredTypeDefiner = processorContext.getSpringPluginBootstrap().autowiredTypeDefiner(); + if(autowiredTypeDefiner != null){ + AutowiredTypeDefinerConfig definerConfig = new AutowiredTypeDefinerConfig(); + autowiredTypeDefiner.config(definerConfig); + classDefiners = definerConfig.getClassDefiners(); + } else { + classDefiners = Collections.emptySet(); + } + } + + @Override + public AutowiredType.Type resolve(DependencyDescriptor descriptor){ + String name = descriptor.getDependencyType().getName(); + String classNamePath = UrlUtils.formatMatchUrl(name); + for (AutowiredTypeDefiner.ClassDefiner classDefiner : classDefiners) { + Set classNamePatterns = classDefiner.getClassNamePatterns(); + for (String classNamePattern : classNamePatterns) { + if(pathMatcher.match(classNamePattern, classNamePath)){ + return classDefiner.getAutowiredType(); + } + } + } + AutowiredType autowiredType = descriptor.getAnnotation(AutowiredType.class); + if(autowiredType != null){ + return autowiredType.value(); + } else { + return AutowiredType.Type.PLUGIN; + } + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java new file mode 100755 index 0000000..28a6b97 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/DefaultSpringPluginHook.java @@ -0,0 +1,120 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.realize.PluginCloseListener; +import com.gitee.starblues.bootstrap.realize.StopValidator; +import com.gitee.starblues.bootstrap.utils.DestroyUtils; +import com.gitee.starblues.bootstrap.utils.SpringBeanUtils; +import com.gitee.starblues.core.PluginCloseType; +import com.gitee.starblues.core.exception.PluginProhibitStopException; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.ApplicationContextProxy; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.WebConfig; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.support.SpringFactoriesLoader; + +import java.util.List; +import java.util.Map; + +/** + * 默认的插件钩子器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class DefaultSpringPluginHook implements SpringPluginHook { + + protected final SpringPluginProcessor pluginProcessor; + protected final ProcessorContext processorContext; + private final StopValidator stopValidator; + + public DefaultSpringPluginHook(SpringPluginProcessor pluginProcessor, + ProcessorContext processorContext) { + this.pluginProcessor = pluginProcessor; + this.processorContext = processorContext; + this.stopValidator = SpringBeanUtils.getExistBean(processorContext.getApplicationContext(), + StopValidator.class); + } + + /** + * 先校验是否可卸载 + */ + @Override + public void stopVerify() { + if(stopValidator == null){ + return; + } + try { + StopValidator.Result result = stopValidator.verify(); + if(result != null && !result.isVerify()){ + throw new PluginProhibitStopException(processorContext.getPluginDescriptor(), + result.getMessage()); + } + } catch (Exception e){ + throw new PluginProhibitStopException(processorContext.getPluginDescriptor(), + e.getMessage()); + } + } + + + @Override + public void close(PluginCloseType closeType) throws Exception{ + try { + GenericApplicationContext applicationContext = processorContext.getApplicationContext(); + callPluginCloseListener(applicationContext, closeType); + pluginProcessor.close(processorContext); + applicationContext.close(); + processorContext.clearRegistryInfo(); + DestroyUtils.destroyAll(null, SpringFactoriesLoader.class, "cache", Map.class); + } catch (Exception e){ + e.printStackTrace(); + } finally { + SpringPluginBootstrapBinder.remove(); + } + } + + @Override + public ApplicationContext getApplicationContext() { + return new ApplicationContextProxy(processorContext.getApplicationContext().getBeanFactory()); + } + + @Override + public WebConfig getWebConfig() { + return processorContext.getWebConfig(); + } + + private void callPluginCloseListener(GenericApplicationContext applicationContext, PluginCloseType closeType){ + List pluginCloseListeners = SpringBeanUtils.getBeans( + applicationContext, PluginCloseListener.class); + if(pluginCloseListeners.isEmpty()){ + return; + } + for (PluginCloseListener pluginCloseListener : pluginCloseListeners) { + try { + pluginCloseListener.close(applicationContext, processorContext.getPluginInfo(), closeType); + } catch (Exception e){ + e.printStackTrace(); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java new file mode 100755 index 0000000..9bbfc0a --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptyMainApplicationContext.java @@ -0,0 +1,106 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.spring.environment.EmptyEnvironmentProvider; +import com.gitee.starblues.spring.environment.EnvironmentProvider; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * 空的MainApplicationContext实现 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class EmptyMainApplicationContext implements MainApplicationContext { + + private final SpringBeanFactory springBeanFactory = new EmptySpringBeanFactory(); + + @Override + public SpringBeanFactory getSpringBeanFactory() { + return springBeanFactory; + } + + @Override + public Object getSourceBeanFactory() { + return null; + } + + @Override + public void close() throws Exception { + + } + + @Override + public Map> getConfigurableEnvironment() { + return Collections.emptyMap(); + } + + @Override + public EnvironmentProvider getEnvironmentProvider() { + return new EmptyEnvironmentProvider(); + } + + @Override + public String[] getActiveProfiles() { + return new String[0]; + } + + @Override + public String[] getDefaultProfiles() { + return new String[0]; + } + + @Override + public Object resolveDependency(String requestingBeanName, Class dependencyType) { + return null; + } + + @Override + public boolean isWebEnvironment() { + return false; + } + + @Override + public Object getSourceApplicationContext() { + return null; + } + + @Override + public boolean isRegisterController() { + return false; + } + + @Override + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + return null; + } + + @Override + public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { + return null; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java new file mode 100755 index 0000000..af375a9 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/EmptySpringBeanFactory.java @@ -0,0 +1,185 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.spring.SpringBeanFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.core.ResolvableType; + +import java.lang.annotation.Annotation; +import java.util.Map; + +/** + * 空的 SpringBeanFactory 实现 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class EmptySpringBeanFactory implements SpringBeanFactory { + @Override + public boolean containsBeanDefinition(String beanName) { + return false; + } + + @Override + public int getBeanDefinitionCount() { + return 0; + } + + @Override + public String[] getBeanDefinitionNames() { + return new String[0]; + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + return null; + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { + return null; + } + + @Override + public String[] getBeanNamesForType(ResolvableType type) { + return new String[0]; + } + + @Override + public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { + return new String[0]; + } + + @Override + public String[] getBeanNamesForType(Class type) { + return new String[0]; + } + + @Override + public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) { + return new String[0]; + } + + @Override + public Map getBeansOfType(Class type) throws BeansException { + return null; + } + + @Override + public Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException { + return null; + } + + @Override + public String[] getBeanNamesForAnnotation(Class annotationType) { + return new String[0]; + } + + @Override + public Map getBeansWithAnnotation(Class annotationType) throws BeansException { + return null; + } + + @Override + public A findAnnotationOnBean(String beanName, Class annotationType) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public A findAnnotationOnBean(String s, Class aClass, boolean b) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public Object getBean(String name) throws BeansException { + return null; + } + + @Override + public T getBean(String name, Class requiredType) throws BeansException { + return null; + } + + @Override + public Object getBean(String name, Object... args) throws BeansException { + return null; + } + + @Override + public T getBean(Class requiredType) throws BeansException { + return null; + } + + @Override + public T getBean(Class requiredType, Object... args) throws BeansException { + return null; + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType) { + return null; + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType) { + return null; + } + + @Override + public boolean containsBean(String name) { + return false; + } + + @Override + public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException { + return false; + } + + @Override + public Class getType(String name) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public Class getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException { + return null; + } + + @Override + public String[] getAliases(String name) { + return new String[0]; + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java new file mode 100755 index 0000000..722e1e6 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginApplicationContext.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * 插件ApplicationContext实现 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class PluginApplicationContext extends AnnotationConfigApplicationContext { + + private final PluginDescriptor pluginDescriptor; + + public PluginApplicationContext(DefaultListableBeanFactory beanFactory, + ProcessorContext processorContext) { + super(beanFactory); + setResourceLoader(processorContext.getResourceLoader()); + this.pluginDescriptor = processorContext.getPluginDescriptor(); + } + + @Override + public void registerShutdownHook() { + // 忽略 + } + + @Override + public String getApplicationName() { + return pluginDescriptor.getPluginId(); + } + + @Override + public void refresh() throws BeansException, IllegalStateException { + super.refresh(); + } + + @Override + public void close() { + super.close(); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginContextHolder.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginContextHolder.java new file mode 100755 index 0000000..e381054 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginContextHolder.java @@ -0,0 +1,122 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.spring.environment.EnvironmentProvider; + +/** + * 提供插件上下文的工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public abstract class PluginContextHolder { + + private static volatile Boolean INITIALIZED = false; + + private static ProcessorContext processorContext; + + private static ClassLoader pluginClassLoader; + private static InsidePluginDescriptor pluginDescriptor; + + private static IntegrationConfiguration configuration; + private static Boolean mainIsWebEnv; + private static SpringBeanFactory mainSpringBeanFactory; + + + private PluginContextHolder(){} + + static void initialize(ProcessorContext processorContext){ + if(INITIALIZED){ + return; + } + PluginContextHolder.processorContext = processorContext; + + PluginContextHolder.pluginClassLoader = processorContext.getClassLoader(); + PluginContextHolder.pluginDescriptor = processorContext.getPluginDescriptor(); + PluginContextHolder.configuration = processorContext.getConfiguration(); + PluginContextHolder.mainIsWebEnv = processorContext.getMainApplicationContext().isWebEnvironment(); + PluginContextHolder.mainSpringBeanFactory = processorContext.getMainBeanFactory(); + INITIALIZED = true; + } + + /** + * 获取主程序环境中配置文件内容提供者 + * @return EnvironmentProvider + */ + public static EnvironmentProvider getEnvironmentProvider(){ + check(); + return processorContext.getMainApplicationContext().getEnvironmentProvider(); + } + + /** + * 获取主程序针对本框架的配置内容 + * @return IntegrationConfiguration + */ + public static IntegrationConfiguration getConfiguration() { + check(); + return configuration; + } + + /** + * 获取主程序的 SpringBeanFactory . 通过它可获取主程序中的Bean + * @return SpringBeanFactory + */ + public static SpringBeanFactory getMainSpringBeanFactory() { + check(); + return mainSpringBeanFactory; + } + + /** + * 判断主程序是否为web环境 + * @return Boolean + */ + public static Boolean getMainIsWebEnv() { + check(); + return mainIsWebEnv; + } + + /** + * 获取插件的 classloader + * @return ClassLoader + */ + public static ClassLoader getPluginClassLoader() { + check(); + return pluginClassLoader; + } + + /** + * 获取插件信息 + * @return InsidePluginDescriptor + */ + public static InsidePluginDescriptor getPluginDescriptor() { + check(); + return pluginDescriptor; + } + + private static void check(){ + if(!INITIALIZED){ + throw new IllegalStateException("PluginContextHolder 未初始化"); + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginDisableAutoConfiguration.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginDisableAutoConfiguration.java new file mode 100755 index 0000000..3f2e9a0 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginDisableAutoConfiguration.java @@ -0,0 +1,146 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.coexist.CoexistAllowAutoConfiguration; +import com.gitee.starblues.common.PluginDisableAutoConfig; +import com.gitee.starblues.loader.launcher.DevelopmentModeSetting; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter; +import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; + +import java.util.*; + +/** + * 插件禁用的 AutoConfiguration + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.3 + */ +public class PluginDisableAutoConfiguration implements AutoConfigurationImportFilter { + + private static final ThreadLocal LAUNCH_PLUGIN = new ThreadLocal(); + + public PluginDisableAutoConfiguration(){ + + } + + public static void setLaunchPlugin() { + LAUNCH_PLUGIN.set(true); + } + + @Override + public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { + if(DevelopmentModeSetting.isolation()){ + return new IsolationDisableAutoConfiguration().match(autoConfigurationClasses, autoConfigurationMetadata); + } else if(DevelopmentModeSetting.coexist()){ + return new CoexistDisableAutoConfiguration().match(autoConfigurationClasses, autoConfigurationMetadata); + } else { + boolean[] permitAll = new boolean[autoConfigurationClasses.length]; + for (int i = 0; i < autoConfigurationClasses.length; i++) { + permitAll[i] = permit( + PluginDisableAutoConfig.getCommonPluginDisableAutoConfig(), + autoConfigurationClasses[i]); + } + return permitAll; + } + } + + private static boolean permit(Collection disableCollection, String className){ + if(ObjectUtils.isEmpty(className)){ + return true; + } + for (String disableFuzzyClass : disableCollection) { + if (className.contains(disableFuzzyClass)) { + return false; + } + } + return true; + } + + private static class IsolationDisableAutoConfiguration implements AutoConfigurationImportFilter{ + + private final List disableFuzzyClass = new ArrayList<>(); + + IsolationDisableAutoConfiguration(){ + addDisableFuzzyClasses(); + } + + private void addDisableFuzzyClasses() { + disableFuzzyClass.add("org.springframework.boot.autoconfigure.http"); + disableFuzzyClass.add("org.springframework.boot.autoconfigure.web"); + disableFuzzyClass.add("org.springframework.boot.autoconfigure.websocket"); + disableFuzzyClass.add("org.springframework.boot.autoconfigure.jackson"); + disableFuzzyClass.add("org.springframework.boot.autoconfigure.webservices"); + disableFuzzyClass.addAll(PluginDisableAutoConfig.getCommonPluginDisableAutoConfig()); + } + + @Override + public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { + boolean[] match = new boolean[autoConfigurationClasses.length]; + for (int i = 0; i < autoConfigurationClasses.length; i++) { + String autoConfigurationClass = autoConfigurationClasses[i]; + if(autoConfigurationClass == null || "".equals(autoConfigurationClass)){ + continue; + } + match[i] = permit(disableFuzzyClass, autoConfigurationClass); + } + return match; + } + } + + + private static class CoexistDisableAutoConfiguration implements AutoConfigurationImportFilter{ + + public CoexistDisableAutoConfiguration(){ + + } + + @Override + public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { + Boolean launchPlugin = LAUNCH_PLUGIN.get(); + boolean[] match = new boolean[autoConfigurationClasses.length]; + try { + if(launchPlugin != null && launchPlugin){ + CoexistAllowAutoConfiguration configuration = SpringPluginBootstrapBinder.get() + .getCoexistAllowAutoConfiguration(); + for (int i = 0; i < autoConfigurationClasses.length; i++) { + String autoConfigurationClass = autoConfigurationClasses[i]; + if(ObjectUtils.isEmpty(autoConfigurationClass)){ + continue; + } + if(permit(PluginDisableAutoConfig.getCommonPluginDisableAutoConfig(), autoConfigurationClass)){ + match[i] = configuration.match(autoConfigurationClass); + } else { + match[i] = false; + } + } + return match; + } else { + for (int i = 0; i < autoConfigurationClasses.length; i++) { + match[i] = true; + } + } + return match; + } finally { + LAUNCH_PLUGIN.remove(); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java new file mode 100755 index 0000000..e20d0e6 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginListableBeanFactory.java @@ -0,0 +1,364 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.utils.DestroyUtils; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.utils.ReflectionUtils; +import lombok.Setter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.lang.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 插件BeanFactory实现 + * + * @author starBlues + * @since 3.0.3 + * @version 3.0.3 + */ +public class PluginListableBeanFactory extends DefaultListableBeanFactory { + + private static final Logger LOG = LoggerFactory.getLogger(PluginListableBeanFactory.class); + + private final MainApplicationContext applicationContext; + + @Setter + private AutowiredTypeResolver autowiredTypeResolver; + + public PluginListableBeanFactory(ProcessorContext processorContext) { + this.applicationContext = processorContext.getMainApplicationContext(); + this.autowiredTypeResolver = getAutowiredTypeResolver(processorContext); + } + + protected AutowiredTypeResolver getAutowiredTypeResolver(ProcessorContext processorContext){ + return new DefaultAutowiredTypeResolver(processorContext); + } + + @SuppressWarnings("unchecked") + @Override + public Object resolveDependency(DependencyDescriptor descriptor, + @Nullable String requestingBeanName, + @Nullable Set autowiredBeanNames, + @Nullable TypeConverter typeConverter) throws BeansException { + AutowiredType.Type autowiredType = getAutowiredType(descriptor); + Class dependencyType = descriptor.getDependencyType(); + if (dependencyType == ObjectFactory.class || dependencyType == ObjectProvider.class) { + Object dependencyObj = super.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, + typeConverter); + ObjectProvider provider = (ObjectProvider) dependencyObj; + return new PluginObjectProviderWrapper(provider, requestingBeanName, descriptor, autowiredType); + } + + if(autowiredType == AutowiredType.Type.MAIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + throw new NoSuchBeanDefinitionException(descriptor.getDependencyType()); + } else if(autowiredType == AutowiredType.Type.PLUGIN){ + return super.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); + } else if(autowiredType == AutowiredType.Type.PLUGIN_MAIN){ + try { + return super.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, + typeConverter); + } catch (BeansException e){ + if(e instanceof NoSuchBeanDefinitionException){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + } + throw e; + } + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj instanceof ObjectProvider){ + ObjectProvider provider = (ObjectProvider) dependencyObj; + return new PluginObjectProviderWrapper(provider, requestingBeanName, descriptor, autowiredType); + } + if(dependencyObj != null){ + return dependencyObj; + } + return super.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, + typeConverter); + } + throw new NoSuchBeanDefinitionException(descriptor.getDependencyType()); + } + + @Override + public void destroySingletons() { + String[] beanDefinitionNames = getBeanDefinitionNames(); + for (String beanDefinitionName : beanDefinitionNames) { + destroyBean(beanDefinitionName); + } + super.destroySingletons(); + destroyAll(); + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + return super.getBeanProvider(requiredType, allowEagerInit); + } + + protected AutowiredType.Type getAutowiredType(DependencyDescriptor descriptor){ + return autowiredTypeResolver.resolve(descriptor); + } + + protected Object resolveDependencyFromMain(String requestingBeanName, DependencyDescriptor descriptor){ + Object dependencyObj = null; + try { + dependencyObj = applicationContext.resolveDependency(requestingBeanName, + descriptor.getDependencyType()); + } catch (Exception e){ + return null; + } + return dependencyObj; + } + + private void destroyAll(){ + ReflectionUtils.findField(this.getClass(), field -> { + field.setAccessible(true); + try { + Object o = field.get(this); + DestroyUtils.destroyAll(o); + } catch (IllegalAccessException e) { + // 忽略 + } + return false; + }); + } + + + private class PluginObjectProviderWrapper implements ObjectProvider { + + private final ObjectProvider pluginObjectProvider; + + private final String requestingBeanName; + private final DependencyDescriptor descriptor; + private final AutowiredType.Type autowiredType; + + private PluginObjectProviderWrapper(ObjectProvider pluginObjectProvider, + String requestingBeanName, + DependencyDescriptor descriptor, + AutowiredType.Type autowiredType) { + this.pluginObjectProvider = pluginObjectProvider; + this.requestingBeanName = requestingBeanName; + this.descriptor = new NestedDependencyDescriptor(descriptor); + this.autowiredType = autowiredType; + } + + @Override + public Object getObject() throws BeansException { + if(autowiredType == AutowiredType.Type.PLUGIN) { + return pluginObjectProvider.getObject(); + } else if(autowiredType == AutowiredType.Type.MAIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + } else if(autowiredType == AutowiredType.Type.PLUGIN_MAIN) { + try { + return pluginObjectProvider.getObject(); + } catch (Exception e) { + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if (dependencyObj != null) { + return dependencyObj; + } + throw e; + } + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + return pluginObjectProvider.getObject(); + } + throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType()); + } + + @Override + public Object getObject(final Object... args) throws BeansException { + if(autowiredType == AutowiredType.Type.PLUGIN){ + return pluginObjectProvider.getObject(args); + } else if(autowiredType == AutowiredType.Type.MAIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + } else if(autowiredType == AutowiredType.Type.PLUGIN_MAIN){ + try { + return pluginObjectProvider.getObject(); + } catch (Exception e){ + try { + return applicationContext.getSpringBeanFactory().getBean(requestingBeanName, args); + } catch (Exception e2){ + // 忽略 + } + throw e; + } + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + return pluginObjectProvider.getObject(args); + } + throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType()); + } + + @Override + @Nullable + public Object getIfAvailable() throws BeansException { + if(autowiredType == AutowiredType.Type.PLUGIN){ + return pluginObjectProvider.getIfAvailable(); + } else if(autowiredType == AutowiredType.Type.MAIN){ + return resolveDependencyFromMain(requestingBeanName, descriptor); + } else if(autowiredType == AutowiredType.Type.PLUGIN_MAIN){ + Object dependencyObj = pluginObjectProvider.getIfAvailable(); + if(dependencyObj == null){ + dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + } + return dependencyObj; + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + return pluginObjectProvider.getIfAvailable(); + } + return null; + } + + @Override + public void ifAvailable(Consumer dependencyConsumer) throws BeansException { + Object ifAvailable = getIfAvailable(); + if(ifAvailable != null){ + dependencyConsumer.accept(ifAvailable); + } + } + + @Override + @Nullable + public Object getIfUnique() throws BeansException { + if(autowiredType == AutowiredType.Type.PLUGIN){ + return pluginObjectProvider.getIfUnique(); + } else if(autowiredType == AutowiredType.Type.MAIN){ + return resolveDependencyFromMain(requestingBeanName, descriptor); + } else if(autowiredType == AutowiredType.Type.PLUGIN_MAIN){ + Object dependencyObj = pluginObjectProvider.getIfUnique(); + if(dependencyObj == null){ + dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + } + return dependencyObj; + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Object dependencyObj = resolveDependencyFromMain(requestingBeanName, descriptor); + if(dependencyObj != null){ + return dependencyObj; + } + return pluginObjectProvider.getIfUnique(); + } + return null; + } + + @Override + public void ifUnique(Consumer dependencyConsumer) throws BeansException { + Object ifUnique = getIfUnique(); + if(ifUnique != null){ + dependencyConsumer.accept(ifUnique); + } + } + + @Override + public Stream stream() { + if(autowiredType == AutowiredType.Type.PLUGIN){ + return pluginObjectProvider.stream(); + } else if(autowiredType == AutowiredType.Type.MAIN){ + return getStreamOfMain().stream(); + } else if (autowiredType == AutowiredType.Type.PLUGIN_MAIN){ + Stream stream = pluginObjectProvider.stream(); + List collect = stream.collect(Collectors.toList()); + if(!collect.isEmpty()){ + return collect.stream(); + } + return getStreamOfMain().stream(); + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Set collection = getStreamOfMain(); + if(!collection.isEmpty()){ + return collection.stream(); + } + return pluginObjectProvider.stream(); + } + return Stream.empty(); + } + + @Override + public Stream orderedStream() { + if(autowiredType == AutowiredType.Type.PLUGIN){ + return pluginObjectProvider.orderedStream(); + } else if(autowiredType == AutowiredType.Type.MAIN){ + return getStreamOfMain().stream().sorted(); + } else if(autowiredType == AutowiredType.Type.PLUGIN_MAIN){ + Stream stream = pluginObjectProvider.stream(); + List collect = stream.collect(Collectors.toList()); + if(!collect.isEmpty()){ + return collect.stream(); + } + return getStreamOfMain().stream().sorted(); + } else if(autowiredType == AutowiredType.Type.MAIN_PLUGIN){ + Set collection = getStreamOfMain(); + if(!collection.isEmpty()){ + return collection.stream().sorted(); + } + return pluginObjectProvider.stream(); + } + return Stream.empty(); + } + + @SuppressWarnings("unchecked") + private Set getStreamOfMain(){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + Map beansOfType = springBeanFactory.getBeansOfType(descriptor.getDependencyType()); + return new HashSet<>(beansOfType.values()); + } + } + + + private static class NestedDependencyDescriptor extends DependencyDescriptor { + + public NestedDependencyDescriptor(DependencyDescriptor original) { + super(original); + increaseNestingLevel(); + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java new file mode 100755 index 0000000..7fc0d3d --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfInteractive.java @@ -0,0 +1,107 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.core.DefaultPluginInsideInfo; +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.descriptor.DevPluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.decrypt.EmptyPluginDescriptorDecrypt; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.integration.AutoIntegrationConfiguration; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.extract.DefaultOpExtractFactory; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.spring.invoke.DefaultInvokeSupperCache; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.utils.FilesUtils; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * 插件自己的Interactive + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class PluginOneselfInteractive implements PluginInteractive { + + private final PluginInsideInfo pluginInsideInfo; + private final MainApplicationContext mainApplicationContext; + private final IntegrationConfiguration configuration; + private final InvokeSupperCache invokeSupperCache; + private final OpExtractFactory opExtractFactory; + + public PluginOneselfInteractive(){ + this.pluginInsideInfo = createPluginInsideInfo(); + this.mainApplicationContext = new EmptyMainApplicationContext(); + this.configuration = new AutoIntegrationConfiguration(); + this.invokeSupperCache = new DefaultInvokeSupperCache(); + this.opExtractFactory = new DefaultOpExtractFactory(); + } + + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginInsideInfo.getPluginDescriptor(); + } + + @Override + public PluginInsideInfo getPluginInsideInfo() { + return pluginInsideInfo; + } + + @Override + public MainApplicationContext getMainApplicationContext() { + return mainApplicationContext; + } + + @Override + public IntegrationConfiguration getConfiguration() { + return configuration; + } + + @Override + public InvokeSupperCache getInvokeSupperCache() { + return invokeSupperCache; + } + + @Override + public OpExtractFactory getOpExtractFactory() { + return opExtractFactory; + } + + private PluginInsideInfo createPluginInsideInfo(){ + EmptyPluginDescriptorDecrypt descriptorDecrypt = new EmptyPluginDescriptorDecrypt(); + try (PluginDescriptorLoader pluginDescriptorLoader = new DevPluginDescriptorLoader(descriptorDecrypt)){ + Path classesPath = Paths.get(this.getClass().getResource("/").toURI()).getParent(); + String metaInf = FilesUtils.joiningFilePath(classesPath.toString(), PackageStructure.META_INF_NAME); + InsidePluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(Paths.get(metaInf)); + if(pluginDescriptor == null){ + throw new RuntimeException("没有发现插件信息, 请使用框架提供的Maven插件器对插件进行编译!"); + } + return new DefaultPluginInsideInfo(pluginDescriptor); + } catch (Exception e){ + throw new RuntimeException(e); + } + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfSpringApplication.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfSpringApplication.java new file mode 100755 index 0000000..76c97c4 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginOneselfSpringApplication.java @@ -0,0 +1,86 @@ +package com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.processor.oneself.ConfigureMainPluginEnvironment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * 插件自主启动的 SpringApplication + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public class PluginOneselfSpringApplication extends SpringApplication { + + private final Logger logger = LoggerFactory.getLogger(PluginSpringApplication.class); + + protected final SpringPluginProcessor pluginProcessor; + protected final ProcessorContext processorContext; + + private final ConfigurePluginEnvironment configurePluginEnvironment; + private final GenericApplicationContext applicationContext; + + + public PluginOneselfSpringApplication(SpringPluginProcessor pluginProcessor, + ProcessorContext processorContext, + Class... primarySources) { + super(primarySources); + this.pluginProcessor = pluginProcessor; + this.processorContext = processorContext; + this.configurePluginEnvironment = new ConfigurePluginEnvironment(processorContext); + this.applicationContext = getApplicationContext(); + } + + protected GenericApplicationContext getApplicationContext() { + return (GenericApplicationContext) super.createApplicationContext(); + } + + @Override + protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + super.configureEnvironment(environment, args); + configurePluginEnvironment.configureEnvironment(environment, args); + new ConfigureMainPluginEnvironment(processorContext).configureEnvironment(environment, args); + } + + @Override + protected void bindToSpringApplication(ConfigurableEnvironment environment) { + super.bindToSpringApplication(environment); + configurePluginEnvironment.logProfiles(environment); + } + + @Override + protected ConfigurableApplicationContext createApplicationContext() { + return this.applicationContext; + } + + @Override + public ConfigurableApplicationContext run(String... args) { + try { + processorContext.setApplicationContext(this.applicationContext); + PluginContextHolder.initialize(processorContext); + pluginProcessor.initialize(processorContext); + return super.run(args); + } catch (Exception e) { + pluginProcessor.failure(processorContext); + logger.debug("启动插件[{}]失败. {}", + processorContext.getPluginDescriptor().getPluginId(), + e.getMessage(), e); + throw new RuntimeException(e); + } + } + + @Override + protected void refresh(ConfigurableApplicationContext applicationContext) { + pluginProcessor.refreshBefore(processorContext); + super.refresh(applicationContext); + pluginProcessor.refreshAfter(processorContext); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java new file mode 100755 index 0000000..28a09ad --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginSpringApplication.java @@ -0,0 +1,126 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.ResourceLoader; + +/** + * 插件SpringApplication实现 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class PluginSpringApplication extends SpringApplication { + + private final Logger logger = LoggerFactory.getLogger(PluginSpringApplication.class); + + protected final SpringPluginProcessor pluginProcessor; + protected final ProcessorContext processorContext; + + private final ConfigurePluginEnvironment configurePluginEnvironment; + private final GenericApplicationContext applicationContext; + private final ResourceLoader resourceLoader; + + + public PluginSpringApplication(SpringPluginProcessor pluginProcessor, + ProcessorContext processorContext, + Class... primarySources) { + super(primarySources); + this.pluginProcessor = pluginProcessor; + this.processorContext = processorContext; + this.resourceLoader = processorContext.getResourceLoader(); + this.configurePluginEnvironment = new ConfigurePluginEnvironment(processorContext); + this.applicationContext = getApplicationContext(); + setDefaultPluginConfig(); + } + + protected GenericApplicationContext getApplicationContext(){ + DefaultListableBeanFactory beanFactory = getBeanFactory(processorContext); + if(processorContext.getMainApplicationContext().isWebEnvironment()){ + return new PluginWebApplicationContext(beanFactory, processorContext); + } else { + return new PluginApplicationContext(beanFactory, processorContext); + } + } + + protected DefaultListableBeanFactory getBeanFactory(ProcessorContext processorContext){ + return new PluginListableBeanFactory(processorContext); + } + + public void setDefaultPluginConfig(){ + setResourceLoader(resourceLoader); + setBannerMode(Banner.Mode.OFF); + setEnvironment(new StandardEnvironment()); + setWebApplicationType(WebApplicationType.NONE); + setRegisterShutdownHook(false); + setLogStartupInfo(false); + } + + @Override + protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + super.configureEnvironment(environment, args); + configurePluginEnvironment.configureEnvironment(environment, args); + } + + @Override + protected void bindToSpringApplication(ConfigurableEnvironment environment) { + super.bindToSpringApplication(environment); + configurePluginEnvironment.logProfiles(environment); + } + + @Override + protected ConfigurableApplicationContext createApplicationContext() { + return this.applicationContext; + } + + @Override + public ConfigurableApplicationContext run(String... args) { + try { + processorContext.setApplicationContext(this.applicationContext); + PluginContextHolder.initialize(processorContext); + pluginProcessor.initialize(processorContext); + return super.run(args); + } catch (Exception e) { + pluginProcessor.failure(processorContext); + logger.debug("启动插件[{}]失败. {}", + processorContext.getPluginDescriptor().getPluginId(), + e.getMessage(), e); + throw new RuntimeException(e); + } + } + + @Override + protected void refresh(ConfigurableApplicationContext applicationContext) { + pluginProcessor.refreshBefore(processorContext); + super.refresh(applicationContext); + pluginProcessor.refreshAfter(processorContext); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginWebApplicationContext.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginWebApplicationContext.java new file mode 100755 index 0000000..5103b0d --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/PluginWebApplicationContext.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.listener.PluginApplicationWebEventListener; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.spring.environment.EnvironmentProvider; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.server.WebServerException; + +/** + * 主程序为 web 类型时创建的插件 ApplicationContext + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class PluginWebApplicationContext extends PluginApplicationContext implements WebServerApplicationContext { + + private final WebServer webServer; + private final String serverNamespace; + + public PluginWebApplicationContext(DefaultListableBeanFactory beanFactory, ProcessorContext processorContext) { + super(beanFactory, processorContext); + this.webServer = new PluginSimulationWebServer(processorContext); + this.serverNamespace = processorContext.getPluginDescriptor().getPluginId(); + addApplicationListener(new PluginApplicationWebEventListener(this)); + } + + @Override + public WebServer getWebServer() { + return webServer; + } + + @Override + public String getServerNamespace() { + return serverNamespace; + } + + + public static class PluginSimulationWebServer implements WebServer { + + private final int port; + + public PluginSimulationWebServer(ProcessorContext processorContext) { + EnvironmentProvider provider = processorContext.getMainApplicationContext().getEnvironmentProvider(); + Integer port = provider.getInteger("server.port"); + if(port == null){ + this.port = -1; + } else { + this.port = port; + } + } + + @Override + public void start() throws WebServerException { + throw new InvalidWebServerException(); + } + + @Override + public void stop() throws WebServerException { + throw new InvalidWebServerException(); + } + + @Override + public int getPort() { + return port; + } + + } + + public static class InvalidWebServerException extends WebServerException{ + + public InvalidWebServerException() { + super("Invalid operation", null); + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ResolveHttpMessageConvertersConfiguration.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ResolveHttpMessageConvertersConfiguration.java new file mode 100755 index 0000000..4fda4dc --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/ResolveHttpMessageConvertersConfiguration.java @@ -0,0 +1,42 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.context.annotation.Bean; +import org.springframework.http.converter.HttpMessageConverter; + +import java.util.stream.Collectors; + +/** + * resolve config HttpMessageConverters bean + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class ResolveHttpMessageConvertersConfiguration { + + @Bean + @ConditionalOnMissingBean + public HttpMessageConverters messageConverters(ObjectProvider> converters) { + return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList())); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java new file mode 100755 index 0000000..6b72302 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrap.java @@ -0,0 +1,122 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap; + +import com.gitee.starblues.bootstrap.coexist.CoexistAllowAutoConfiguration; +import com.gitee.starblues.bootstrap.launcher.*; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.realize.AutowiredTypeDefiner; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.spring.SpringPluginHook; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 插件引导抽象类。插件入口需集成本抽象类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public abstract class SpringPluginBootstrap { + + @Getter + private ProcessorContext.RunMode runMode = ProcessorContext.RunMode.ONESELF; + @Getter + private volatile PluginInteractive pluginInteractive; + @Getter + private final List customPluginProcessors = new ArrayList<>(); + @Getter + private final CoexistAllowAutoConfiguration coexistAllowAutoConfiguration = new CoexistAllowAutoConfiguration(); + + private final BootstrapLauncherFactory launcherFactory = new DefaultBootstrapLauncherFactory(); + + public SpringPluginBootstrap() { + SpringPluginBootstrapBinder.set(this); + } + + public final SpringPluginHook run(String[] args){ + return run(this.getClass(), args); + } + + public final SpringPluginHook run(Class primarySources, String[] args){ + return run(new Class[]{ primarySources }, args); + } + + public final SpringPluginHook run(Class[] primarySources, String[] args){ + return start(primarySources, args); + } + + private SpringPluginHook start(Class[] primarySources, String[] args){ + configCoexistAllowAutoConfiguration(this.coexistAllowAutoConfiguration); + createPluginInteractive(); + addCustomSpringPluginProcessor(); + BootstrapLauncher bootstrapLauncher = launcherFactory.create(this); + return bootstrapLauncher.launch(primarySources, args); + } + + public final SpringPluginBootstrap setPluginInteractive(PluginInteractive pluginInteractive) { + this.pluginInteractive = pluginInteractive; + this.runMode = ProcessorContext.RunMode.PLUGIN; + return this; + } + + public final SpringPluginBootstrap addSpringPluginProcessor(SpringPluginProcessor springPluginProcessor){ + if(springPluginProcessor != null){ + customPluginProcessors.add(springPluginProcessor); + } + return this; + } + + protected final void createPluginInteractive(){ + if(pluginInteractive != null){ + return; + } + createPluginInteractiveOfOneself(); + } + + protected final void createPluginInteractiveOfOneself(){ + this.pluginInteractive = new PluginOneselfInteractive(); + } + + + /** + * 子类自定义插件 SpringPluginProcessor + */ + protected void addCustomSpringPluginProcessor(){} + + /** + * 设置 AutowiredTypeDefiner + * @return AutowiredTypeDefiner + * @since 3.0.3 + */ + protected AutowiredTypeDefiner autowiredTypeDefiner(){ + return null; + } + + /** + * 在 Coexist 模式下手动配置 spring-boot-auto-configuration 类 + * @param configuration 配置的类 + */ + protected void configCoexistAllowAutoConfiguration(CoexistAllowAutoConfiguration configuration){ + + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrapBinder.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrapBinder.java new file mode 100755 index 0000000..da40ce7 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/SpringPluginBootstrapBinder.java @@ -0,0 +1,28 @@ +package com.gitee.starblues.bootstrap; + +/** + * SpringPluginBootstrap 实例绑者 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class SpringPluginBootstrapBinder { + + private final static ThreadLocal BINDER = new ThreadLocal<>(); + + + public static SpringPluginBootstrap get(){ + return BINDER.get(); + } + + public static void set(SpringPluginBootstrap bootstrap){ + BINDER.set(bootstrap); + } + + public static void remove(){ + BINDER.remove(); + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/AutowiredType.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/AutowiredType.java new file mode 100755 index 0000000..fcf7923 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/AutowiredType.java @@ -0,0 +1,48 @@ +package com.gitee.starblues.bootstrap.annotation; + + +import java.lang.annotation.*; + +/** + * 注入类型 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AutowiredType { + + /** + * 插件Bean注入类型 + * @return Type + */ + Type value() default Type.PLUGIN; + + + enum Type{ + /** + * Bean 注入类型: 仅插件(默认) + */ + PLUGIN, + + /** + * Bean 注入类型: 仅主程序 + */ + MAIN, + + /** + * Bean 注入类型: 先插件后主程序 + */ + PLUGIN_MAIN, + + /** + * Bean 注入类型: 先主程序后插件 + */ + MAIN_PLUGIN + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java new file mode 100755 index 0000000..c4098d5 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/DisablePluginWeb.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.annotation; + +import java.lang.annotation.*; + +/** + * 禁用web环境. 如果该注解加入到入口类上, 表示当前插件禁用web的功能。 + * 包括:controller注册、拦截器注册、web静态资源访问、thymeleaf模板引擎 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DisablePluginWeb { +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java new file mode 100755 index 0000000..d51fb67 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/OneselfConfig.java @@ -0,0 +1,40 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.annotation; + +import java.lang.annotation.*; + +/** + * 插件自主运行配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OneselfConfig { + + /** + * 主程序配置文件名称 + * @return String[] + */ + String[] mainConfigFileName() default {}; + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/ResolveClassLoader.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/ResolveClassLoader.java new file mode 100755 index 0000000..f2617cd --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/annotation/ResolveClassLoader.java @@ -0,0 +1,32 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.annotation; + +import java.lang.annotation.*; + +/** + * 解决方法级别调用时, 当前线程非本插件的ClassLoader注解 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ResolveClassLoader { +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistAllowAutoConfiguration.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistAllowAutoConfiguration.java new file mode 100755 index 0000000..4d8628c --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistAllowAutoConfiguration.java @@ -0,0 +1,60 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.coexist; + +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * Coexist模式下存储当前插件允许的 auto 配置 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class CoexistAllowAutoConfiguration { + + private final Set allowPrefix = new HashSet<>(); + + public CoexistAllowAutoConfiguration(){ + addDefault(); + } + + private void addDefault(){ + allowPrefix.add("org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"); + } + + public CoexistAllowAutoConfiguration add(String autoConfigurationClass){ + if(ObjectUtils.isEmpty(autoConfigurationClass)){ + return this; + } + allowPrefix.add(autoConfigurationClass); + return this; + } + + public boolean match(String autoConfigurationClass){ + for (String prefix : allowPrefix) { + if(autoConfigurationClass.startsWith(prefix)){ + return true; + } + } + return false; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistResolveClassLoaderAspect.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistResolveClassLoaderAspect.java new file mode 100755 index 0000000..df96584 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/coexist/CoexistResolveClassLoaderAspect.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.coexist; + +import com.gitee.starblues.bootstrap.annotation.ResolveClassLoader; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +/** + * Coexist模式下解决调用方法时, 非本插件的ClassLoader + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +@Aspect +public class CoexistResolveClassLoaderAspect { + + @Pointcut("@annotation(com.gitee.starblues.bootstrap.annotation.ResolveClassLoader)") + public void test() { + + } + + @Around("@annotation(resolveClassLoader)") + public Object around(ProceedingJoinPoint pjp, ResolveClassLoader resolveClassLoader) throws Throwable{ + Thread thread = Thread.currentThread(); + ClassLoader oldClassLoader = thread.getContextClassLoader(); + try { + Object target = pjp.getTarget(); + thread.setContextClassLoader(target.getClass().getClassLoader()); + return pjp.proceed(); + } finally { + thread.setContextClassLoader(oldClassLoader); + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncher.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncher.java new file mode 100755 index 0000000..128d093 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncher.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.launcher; + +import com.gitee.starblues.spring.SpringPluginHook; + +/** + * 插件启动器 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public interface BootstrapLauncher { + + /** + * 启动插件 + * @param primarySources 主启动类 + * @param args 启动参数 + * @return SpringPluginHook + */ + SpringPluginHook launch(Class[] primarySources, String[] args); + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncherFactory.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncherFactory.java new file mode 100755 index 0000000..a40de52 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/BootstrapLauncherFactory.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.launcher; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; + +/** + * BootstrapLauncher 创造工厂 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public interface BootstrapLauncherFactory { + + /** + * 创造 BootstrapLauncher + * @param bootstrap SpringPluginBootstrap + * @return BootstrapLauncher + */ + BootstrapLauncher create(SpringPluginBootstrap bootstrap); + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/CoexistBootstrapLauncher.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/CoexistBootstrapLauncher.java new file mode 100755 index 0000000..dc226e6 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/CoexistBootstrapLauncher.java @@ -0,0 +1,111 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.launcher; + +import com.gitee.starblues.bootstrap.*; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.gitee.starblues.bootstrap.coexist.CoexistResolveClassLoaderAspect; +import com.gitee.starblues.bootstrap.processor.DefaultProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.spring.SpringPluginHook; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * Coexist 类型启动器 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + * @see com.gitee.starblues.loader.DevelopmentMode#COEXIST + */ +@AllArgsConstructor +public class CoexistBootstrapLauncher implements BootstrapLauncher{ + + private final SpringPluginBootstrap bootstrap; + private final SpringPluginProcessor pluginProcessor; + private final PluginInteractive pluginInteractive; + + @Override + public SpringPluginHook launch(Class[] primarySources, String[] args) { + ProcessorContext processorContext = new DefaultProcessorContext( + bootstrap.getRunMode(), bootstrap, pluginInteractive, bootstrap.getClass() + ); + SpringApplication springApplication = new CoexistSpringApplication( + pluginProcessor, + processorContext, + primarySources); + springApplication.run(args); + return new DefaultSpringPluginHook(pluginProcessor, processorContext); + } + + private static class CoexistSpringApplication extends PluginSpringApplication{ + + public CoexistSpringApplication(SpringPluginProcessor pluginProcessor, ProcessorContext processorContext, Class... primarySources) { + super(pluginProcessor, processorContext, primarySources); + } + + @Override + protected DefaultListableBeanFactory getBeanFactory(ProcessorContext processorContext) { + return new CoexistPluginListableBeanFactory(processorContext); + } + + @Override + protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + super.configureEnvironment(environment, args); + } + + @Override + protected GenericApplicationContext getApplicationContext() { + PluginApplicationContext applicationContext = (PluginApplicationContext) super.getApplicationContext(); + applicationContext.register(CoexistResolveClassLoaderAspect.class); + return applicationContext; + } + } + + private static class CoexistPluginListableBeanFactory extends PluginListableBeanFactory{ + + public CoexistPluginListableBeanFactory(ProcessorContext processorContext) { + super(processorContext); + } + + @Override + protected AutowiredTypeResolver getAutowiredTypeResolver(ProcessorContext processorContext) { + return new CoexistAutowiredTypeResolver(); + } + } + + private static class CoexistAutowiredTypeResolver implements AutowiredTypeResolver{ + + @Override + public AutowiredType.Type resolve(DependencyDescriptor descriptor) { + AutowiredType autowiredType = descriptor.getAnnotation(AutowiredType.class); + if(autowiredType != null){ + return autowiredType.value(); + } else { + return AutowiredType.Type.PLUGIN; + } + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/DefaultBootstrapLauncherFactory.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/DefaultBootstrapLauncherFactory.java new file mode 100755 index 0000000..6869072 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/DefaultBootstrapLauncherFactory.java @@ -0,0 +1,55 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.launcher; + +import com.gitee.starblues.bootstrap.PluginDisableAutoConfiguration; +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.bootstrap.processor.ComposeSpringPluginProcessor; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.loader.launcher.DevelopmentModeSetting; + +import java.util.List; + +/** + * 默认的 BootstrapLauncher 创造工厂 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class DefaultBootstrapLauncherFactory implements BootstrapLauncherFactory{ + @Override + public BootstrapLauncher create(SpringPluginBootstrap bootstrap) { + PluginDisableAutoConfiguration.setLaunchPlugin(); + ProcessorContext.RunMode runMode = bootstrap.getRunMode(); + List customPluginProcessors = bootstrap.getCustomPluginProcessors(); + PluginInteractive pluginInteractive = bootstrap.getPluginInteractive(); + + SpringPluginProcessor pluginProcessor = new ComposeSpringPluginProcessor(runMode, customPluginProcessors); + BootstrapLauncher bootstrapLauncher = null; + if(DevelopmentModeSetting.isolation()){ + bootstrapLauncher = new IsolationBootstrapLauncher(bootstrap, pluginProcessor, pluginInteractive); + } else if(DevelopmentModeSetting.coexist()){ + bootstrapLauncher = new CoexistBootstrapLauncher(bootstrap, pluginProcessor, pluginInteractive); + } else { + bootstrapLauncher = new OneselfBootstrapLauncher(bootstrap, pluginProcessor, pluginInteractive); + } + return bootstrapLauncher; + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/IsolationBootstrapLauncher.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/IsolationBootstrapLauncher.java new file mode 100755 index 0000000..4a20305 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/IsolationBootstrapLauncher.java @@ -0,0 +1,59 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.launcher; + +import com.gitee.starblues.bootstrap.DefaultSpringPluginHook; +import com.gitee.starblues.bootstrap.PluginSpringApplication; +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.bootstrap.processor.DefaultProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.spring.SpringPluginHook; +import lombok.AllArgsConstructor; +import org.springframework.boot.SpringApplication; + +/** + * isolation 模式插件启动器 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + * @see com.gitee.starblues.loader.DevelopmentMode#ISOLATION + */ +@AllArgsConstructor +public class IsolationBootstrapLauncher implements BootstrapLauncher{ + + private final SpringPluginBootstrap bootstrap; + private final SpringPluginProcessor pluginProcessor; + private final PluginInteractive pluginInteractive; + + @Override + public SpringPluginHook launch(Class[] primarySources, String[] args) { + ProcessorContext.RunMode runMode = bootstrap.getRunMode(); + + ProcessorContext processorContext = new DefaultProcessorContext( + runMode, bootstrap, pluginInteractive, bootstrap.getClass() + ); + SpringApplication springApplication = new PluginSpringApplication( + pluginProcessor, + processorContext, + primarySources); + springApplication.run(args); + return new DefaultSpringPluginHook(pluginProcessor, processorContext); + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/OneselfBootstrapLauncher.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/OneselfBootstrapLauncher.java new file mode 100755 index 0000000..d0f4dcd --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/launcher/OneselfBootstrapLauncher.java @@ -0,0 +1,47 @@ +package com.gitee.starblues.bootstrap.launcher; + +import com.gitee.starblues.bootstrap.DefaultSpringPluginHook; +import com.gitee.starblues.bootstrap.PluginOneselfSpringApplication; +import com.gitee.starblues.bootstrap.PluginSpringApplication; +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.bootstrap.processor.DefaultProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.spring.SpringPluginHook; +import lombok.AllArgsConstructor; +import org.springframework.boot.SpringApplication; + +/** + * 插件自主启动配置 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +@AllArgsConstructor +public class OneselfBootstrapLauncher implements BootstrapLauncher{ + + private final SpringPluginBootstrap bootstrap; + private final SpringPluginProcessor pluginProcessor; + private final PluginInteractive pluginInteractive; + + + @Override + public SpringPluginHook launch(Class[] primarySources, String[] args) { + ProcessorContext.RunMode runMode = bootstrap.getRunMode(); + + ProcessorContext processorContext = new DefaultProcessorContext( + runMode, bootstrap, pluginInteractive, bootstrap.getClass() + ); + SpringApplication springApplication = new PluginOneselfSpringApplication( + pluginProcessor, + processorContext, + primarySources); + springApplication.run(args); + return new DefaultSpringPluginHook(pluginProcessor, processorContext); + } + + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/listener/PluginApplicationWebEventListener.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/listener/PluginApplicationWebEventListener.java new file mode 100755 index 0000000..cccd412 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/listener/PluginApplicationWebEventListener.java @@ -0,0 +1,83 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.listener; + +import com.gitee.starblues.bootstrap.PluginWebApplicationContext; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.web.context.WebServerApplicationContext; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.core.ResolvableType; + +/** + * 插件监听器扩展 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class PluginApplicationWebEventListener implements ApplicationListener { + + + private final PluginWebApplicationContext applicationContext; + + public PluginApplicationWebEventListener(PluginWebApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationReadyEvent) { + callWebServerInitializedEvent(); + } + } + + @SuppressWarnings("all") + protected void callWebServerInitializedEvent(){ + String[] beanNamesForType = applicationContext.getBeanNamesForType(ResolvableType.forClassWithGenerics( + ApplicationListener.class, WebServerInitializedEvent.class + )); + PluginWebServerInitializedEvent pluginWebServerInitializedEvent = + new PluginWebServerInitializedEvent(applicationContext); + for (String beanName : beanNamesForType) { + try { + ApplicationListener applicationListener = + (ApplicationListener) applicationContext.getBean(beanName); + applicationListener.onApplicationEvent(pluginWebServerInitializedEvent); + } catch (Exception e){ + e.printStackTrace(); + } + } + } + + public static class PluginWebServerInitializedEvent extends WebServerInitializedEvent{ + + private final PluginWebApplicationContext pluginWebApplicationContext; + + protected PluginWebServerInitializedEvent(PluginWebApplicationContext pluginWebApplicationContext) { + super(pluginWebApplicationContext.getWebServer()); + this.pluginWebApplicationContext = pluginWebApplicationContext; + } + + @Override + public WebServerApplicationContext getApplicationContext() { + return pluginWebApplicationContext; + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java new file mode 100755 index 0000000..5009d84 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ComposeSpringPluginProcessor.java @@ -0,0 +1,202 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.bootstrap.annotation.DisablePluginWeb; +import com.gitee.starblues.bootstrap.processor.oneself.OneselfProcessor; +import com.gitee.starblues.bootstrap.processor.web.PluginControllerProcessor; +import com.gitee.starblues.bootstrap.processor.web.PluginInterceptorsProcessor; +import com.gitee.starblues.bootstrap.processor.web.PluginStaticResourceProcessor; +import com.gitee.starblues.bootstrap.utils.AnnotationUtils; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.OrderPriority; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 组合的处理器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class ComposeSpringPluginProcessor implements SpringPluginProcessor { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final ProcessorContext.RunMode runMode; + + private List processors; + + public ComposeSpringPluginProcessor(ProcessorContext.RunMode runMode) { + this(runMode, null); + } + + public ComposeSpringPluginProcessor(ProcessorContext.RunMode runMode, List processors) { + this.runMode = runMode; + if(!ObjectUtils.isEmpty(processors)){ + this.processors = processors; + } else { + this.processors = new ArrayList<>(); + } + } + + public void addSpringPluginProcessor(SpringPluginProcessor springPluginProcessor){ + if(springPluginProcessor != null){ + this.processors.add(springPluginProcessor); + } + } + + @Override + public void initialize(ProcessorContext context) throws ProcessorException { + List processors = new ArrayList<>(); + addDefaultProcessors(context, processors); + addDefaultWebEnvProcessors(context, processors); + addOneselfProcessors(context, processors); + processors.addAll(this.processors); + this.processors = processors.stream() + .filter(p->{ + ProcessorContext.RunMode runMode = p.runMode(); + return runMode == ProcessorContext.RunMode.ALL || runMode == this.runMode; + }) + .sorted(OrderUtils.orderPriority(SpringPluginProcessor::order)) + .collect(Collectors.toList()); + for (SpringPluginProcessor processor : this.processors) { + try { + processor.initialize(context); + } catch (Throwable e){ + processException(processor, "initialize", e, true); + } + } + } + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.refreshBefore(context); + } catch (Throwable e){ + processException(processor, "refreshBefore", e, true); + } + } + } + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.refreshAfter(context); + } catch (Throwable e){ + processException(processor, "refreshAfter", e, true); + } + } + } + + @Override + public void failure(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.failure(context); + } catch (Throwable e){ + processException(processor, "failure", e, false); + } + } + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + for (SpringPluginProcessor processor : processors) { + try { + processor.close(context); + } catch (Throwable e){ + processException(processor, "close", e, false); + } + } + } + + @Override + public OrderPriority order() { + return OrderPriority.getHighPriority(); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } + + /** + * 获取默认的处理者 + * @param context ProcessorContext + * @param processors 处理者容器集合 + */ + protected void addDefaultProcessors(ProcessorContext context, List processors){ + processors.add(new FrameDefineBeanProcessor()); + processors.add(new ExtractBeanProcessor()); + processors.add(new InvokeOtherPluginProcessor()); + } + + /** + * 添加默认web环境处理者 + * @param context ProcessorContext + * @param processors 处理者容器集合 + */ + protected void addDefaultWebEnvProcessors(ProcessorContext context, List processors){ + MainApplicationContext mainApplicationContext = context.getMainApplicationContext(); + if(!mainApplicationContext.isWebEnvironment()){ + // 主程序不是web类型, 则不进行注册 + return; + } + SpringPluginBootstrap springPluginBootstrap = context.getSpringPluginBootstrap(); + DisablePluginWeb disablePluginWeb = AnnotationUtils.findAnnotation(springPluginBootstrap.getClass(), + DisablePluginWeb.class); + if(disablePluginWeb != null){ + return; + } + context.getWebConfig().setEnable(true); + if(mainApplicationContext.isRegisterController()){ + processors.add(new PluginControllerProcessor()); + } + processors.add(new PluginInterceptorsProcessor()); + processors.add(new PluginStaticResourceProcessor()); + } + + /** + * 添加 Oneself 模式处理者 + * @param context ProcessorContext + * @param processors 处理者容器集合 + */ + private void addOneselfProcessors(ProcessorContext context, List processors) { + processors.add(new OneselfProcessor()); + } + + private void processException(SpringPluginProcessor processor, String executeType, + Throwable e, boolean isThrow) throws ProcessorException{ + String error = "Processor[" + processor.getClass().getName() + "] execute[" + executeType + "] failure : " + + e.getMessage(); + logger.error(error, e); + if(isThrow){ + throw new ProcessorException(error, e); + } + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java new file mode 100755 index 0000000..db6e4ef --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/DefaultProcessorContext.java @@ -0,0 +1,150 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.launcher.plugin.CacheRegistryInfo; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.spring.WebConfig; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.ClassUtils; + +import java.util.Map; + +/** + * 默认的处理者上下文 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DefaultProcessorContext extends CacheRegistryInfo implements ProcessorContext{ + + private final RunMode runMode; + + private final SpringPluginBootstrap springPluginBootstrap; + private final PluginInteractive pluginInteractive; + private final Class runnerClass; + private final MainApplicationContext mainApplicationContext; + private final ClassLoader classLoader; + private final ResourceLoader resourceLoader; + + private final IntegrationConfiguration configuration; + private final WebConfig webConfig; + + private GenericApplicationContext applicationContext; + + public DefaultProcessorContext(RunMode runMode, SpringPluginBootstrap springPluginBootstrap, + PluginInteractive pluginInteractive, Class runnerClass) { + this.runMode = runMode; + this.springPluginBootstrap = springPluginBootstrap; + this.pluginInteractive = pluginInteractive; + this.runnerClass = runnerClass; + this.classLoader = getPluginClassLoader(); + this.resourceLoader = new DefaultResourceLoader(this.classLoader); + this.mainApplicationContext = pluginInteractive.getMainApplicationContext(); + this.configuration = pluginInteractive.getConfiguration(); + this.webConfig = new WebConfig(); + } + + @Override + public RunMode runMode() { + return runMode; + } + + @Override + public SpringPluginBootstrap getSpringPluginBootstrap() { + return springPluginBootstrap; + } + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginInteractive.getPluginDescriptor(); + } + + @Override + public PluginInfo getPluginInfo() { + return pluginInteractive.getPluginInsideInfo(); + } + + @Override + public Class getRunnerClass() { + return runnerClass; + } + + @Override + public PluginInteractive getPluginInteractive() { + return pluginInteractive; + } + + @Override + public MainApplicationContext getMainApplicationContext() { + return mainApplicationContext; + } + + @Override + public SpringBeanFactory getMainBeanFactory() { + return mainApplicationContext.getSpringBeanFactory(); + } + + @Override + public IntegrationConfiguration getConfiguration() { + return configuration; + } + + @Override + public GenericApplicationContext getApplicationContext() { + if(applicationContext == null){ + return null; + } + return applicationContext; + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public ResourceLoader getResourceLoader() { + return resourceLoader; + } + + @Override + public WebConfig getWebConfig() { + return webConfig; + } + + @Override + public void setApplicationContext(GenericApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + protected ClassLoader getPluginClassLoader(){ + return ClassUtils.getDefaultClassLoader(); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java new file mode 100755 index 0000000..303f532 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ExtractBeanProcessor.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.annotation.Extract; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.Map; + +/** + * Extract 扩展Bean注册处理者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ExtractBeanProcessor implements SpringPluginProcessor { + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + Map extractMap = applicationContext.getBeansWithAnnotation(Extract.class); + if(ObjectUtils.isEmpty(extractMap)){ + return; + } + String pluginId = context.getPluginDescriptor().getPluginId(); + OpExtractFactory opExtractFactory = context.getPluginInteractive().getOpExtractFactory(); + for (Object extract : extractMap.values()) { + opExtractFactory.add(pluginId, extract); + } + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + OpExtractFactory opExtractFactory = context.getPluginInteractive().getOpExtractFactory(); + String pluginId = context.getPluginDescriptor().getPluginId(); + opExtractFactory.remove(pluginId); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java new file mode 100755 index 0000000..d61ecae --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/FrameDefineBeanProcessor.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.AutoIntegrationConfiguration; +import com.gitee.starblues.integration.ExtendPointConfiguration; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 框架内置bean注册 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class FrameDefineBeanProcessor implements SpringPluginProcessor { + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + InsidePluginDescriptor pluginDescriptor = context.getPluginDescriptor(); + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + beanFactory.registerSingleton("pluginDescriptor", pluginDescriptor.toPluginDescriptor()); + beanFactory.registerSingleton("pluginInfo", context.getPluginInfo()); + beanFactory.registerSingleton("mainApplicationContext", context.getMainApplicationContext()); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java new file mode 100755 index 0000000..3888513 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/InvokeOtherPluginProcessor.java @@ -0,0 +1,128 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.annotation.Caller; +import com.gitee.starblues.annotation.Supplier; +import com.gitee.starblues.bootstrap.processor.invoke.InvokeBeanFactory; +import com.gitee.starblues.bootstrap.processor.scanner.PluginClassPathBeanDefinitionScanner; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.ApplicationContextProxy; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.spring.invoke.SupperCache; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ScanUtils; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.type.filter.AnnotationTypeFilter; + +import java.util.Map; +import java.util.Set; + +/** + * 反射调用其他插件的处理者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class InvokeOtherPluginProcessor implements SpringPluginProcessor { + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + InvokeCallerBeanDefinitionScanner scanner = new InvokeCallerBeanDefinitionScanner(context); + scanner.doScan(ScanUtils.getScanBasePackages(context.getRunnerClass())); + } + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + Map supplierBeans = applicationContext.getBeansWithAnnotation(Supplier.class); + String pluginId = context.getPluginDescriptor().getPluginId(); + ApplicationContext applicationContextReflection = new ApplicationContextProxy(applicationContext); + InvokeSupperCache invokeSupperCache = context.getPluginInteractive().getInvokeSupperCache(); + supplierBeans.forEach((k,v)->{ + Supplier supplier = AnnotationUtils.findAnnotation(v.getClass(), Supplier.class); + String supperKey = k; + if(supplier != null && !ObjectUtils.isEmpty(supplier.value())){ + supperKey = supplier.value(); + } + invokeSupperCache.add(pluginId, new SupperCache(supperKey, k, applicationContextReflection)); + }); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ALL; + } + + + private static class InvokeCallerBeanDefinitionScanner extends PluginClassPathBeanDefinitionScanner { + + private final ProcessorContext context; + + public InvokeCallerBeanDefinitionScanner(ProcessorContext context) { + super(context, false); + setResourceLoader(context.getResourceLoader()); + this.context = context; + addIncludeFilter(new AnnotationTypeFilter(Caller.class)); + addExcludeFilter((metadataReader, metadataReaderFactory) -> { + String className = metadataReader.getClassMetadata().getClassName(); + return className.endsWith("package-info"); + }); + } + + + @Override + protected Set doScan(String... basePackages) { + Set holders = super.doScan(basePackages); + ClassLoader pluginClassLoader = context.getClassLoader(); + InvokeSupperCache invokeSupperCache = context.getPluginInteractive().getInvokeSupperCache(); + for (BeanDefinitionHolder holder : holders) { + AbstractBeanDefinition definition = (AbstractBeanDefinition) holder.getBeanDefinition(); + try { + Class aClass = pluginClassLoader.loadClass(definition.getBeanClassName()); + Caller caller = AnnotationUtils.findAnnotation(aClass, Caller.class); + if(caller == null){ + continue; + } + // 是调用方 + definition.getPropertyValues().add("callerAnnotation", caller); + definition.getPropertyValues().add("callerInterface", aClass); + definition.getPropertyValues().add("invokeSupperCache", invokeSupperCache); + definition.setBeanClass(InvokeBeanFactory.class); + definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + return holders; + } + + @Override + protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { + return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); + } + + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java new file mode 100755 index 0000000..15dd3df --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorContext.java @@ -0,0 +1,148 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.core.launcher.plugin.RegistryInfo; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.spring.WebConfig; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.io.ResourceLoader; + +/** + * 处理者上下文 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface ProcessorContext extends RegistryInfo { + + /** + * 当前运行模式 + * @return RunMode + */ + RunMode runMode(); + + /** + * 得到入口类对象-SpringPluginBootstrap + * @return SpringPluginBootstrap + */ + SpringPluginBootstrap getSpringPluginBootstrap(); + + /** + * 得到插件信息 PluginDescriptor + * @return PluginDescriptor + */ + InsidePluginDescriptor getPluginDescriptor(); + + /** + * 得到插件信息 PluginInfo + * @return PluginInfo + */ + PluginInfo getPluginInfo(); + + /** + * 得到启动的class类 + * @return Class + */ + Class getRunnerClass(); + + /** + * 得到 PluginInteractive + * @return PluginInteractive + */ + PluginInteractive getPluginInteractive(); + + /** + * 得到主程序的 ApplicationContext + * @return MainApplicationContext + */ + MainApplicationContext getMainApplicationContext(); + + /** + * 得到主程序的 SpringBeanFactory + * @return SpringBeanFactory + */ + SpringBeanFactory getMainBeanFactory(); + + /** + * 得到当前框架的集成配置 + * @return IntegrationConfiguration + */ + IntegrationConfiguration getConfiguration(); + + + /** + * 得到当前插件的 ApplicationContext + * @return GenericApplicationContext + */ + GenericApplicationContext getApplicationContext(); + + /** + * 得到当前插件的 ClassLoader + * @return ClassLoader + */ + ClassLoader getClassLoader(); + + /** + * 得到插件的资源loader + * @return ResourceLoader + */ + ResourceLoader getResourceLoader(); + + /** + * 获取 WebConfig + * @return WebConfig + */ + WebConfig getWebConfig(); + + /** + * set 当前插件的 ApplicationContext + * @param applicationContext GenericApplicationContext + */ + void setApplicationContext(GenericApplicationContext applicationContext); + + /** + * 运行模式 + */ + enum RunMode{ + /** + * 全部运行 + */ + ALL, + + /** + * 插件环境运行 + */ + PLUGIN, + + /** + * 插件独立运行 + */ + ONESELF + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java new file mode 100755 index 0000000..8dd7532 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/ProcessorException.java @@ -0,0 +1,47 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + +/** + * 处理者异常 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ProcessorException extends RuntimeException{ + + public ProcessorException() { + super(); + } + + public ProcessorException(String message) { + super(message); + } + + public ProcessorException(String message, Throwable cause) { + super(message, cause); + } + + public ProcessorException(Throwable cause) { + super(cause); + } + + protected ProcessorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java new file mode 100755 index 0000000..5117f2b --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/SpringPluginProcessor.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor; + +import com.gitee.starblues.utils.Order; +import com.gitee.starblues.utils.OrderPriority; + +/** + * spring plugin 处理器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface SpringPluginProcessor extends Order { + + + /** + * 初始化时 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void initialize(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 刷新上下文前 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void refreshBefore(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 刷新上下文后 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void refreshAfter(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 启动失败 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void failure(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 关闭容器时 + * @param context ProcessorContext + * @throws ProcessorException 处理异常 + */ + default void close(ProcessorContext context) throws ProcessorException{ + + } + + /** + * 执行顺序 + * @return OrderPriority + */ + @Override + default OrderPriority order(){ + return OrderPriority.getLowPriority(); + } + + /** + * 处理器运行模式 + * @return RunMode + */ + ProcessorContext.RunMode runMode(); + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java new file mode 100755 index 0000000..7cd194f --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegister.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.interceptor; + +/** + * 插件拦截器注册者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginInterceptorRegister { + + /** + * 拦截器注册者 + * @param registry 注册对象 + */ + void registry(PluginInterceptorRegistry registry); + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java new file mode 100755 index 0000000..6d32f85 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistration.java @@ -0,0 +1,174 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.interceptor; + +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.UrlUtils; +import org.springframework.lang.Nullable; +import org.springframework.util.PathMatcher; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistration; +import org.springframework.web.servlet.handler.MappedInterceptor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 插件拦截器注册的信息 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginInterceptorRegistration { + + private final HandlerInterceptor interceptor; + private final PluginInterceptorRegistry.Type type; + private final String pluginRestApiPrefix; + + private final List includePatterns = new ArrayList<>(); + + private final List excludePatterns = new ArrayList<>(); + + @Nullable + private PathMatcher pathMatcher; + + private int order = 0; + + /** + * Create an {@link InterceptorRegistration} instance. + * + * @param interceptor 拦截器 + * @param type 类型 + * @param pluginRestApiPrefix 接口前缀 + */ + public PluginInterceptorRegistration(HandlerInterceptor interceptor, + PluginInterceptorRegistry.Type type, + String pluginRestApiPrefix) { + this.interceptor = interceptor; + this.type = type; + String apiPrefix = null; + if(pluginRestApiPrefix.startsWith("/")){ + apiPrefix = pluginRestApiPrefix; + } else { + apiPrefix = "/" + pluginRestApiPrefix; + } + if(apiPrefix.endsWith("/")){ + apiPrefix = apiPrefix.substring(0, apiPrefix.lastIndexOf("/")); + } + this.pluginRestApiPrefix = apiPrefix; + } + + + /** + * Add URL patterns to which the registered interceptor should apply to. + * @param patterns patterns + * @return PluginInterceptorRegistration + */ + public PluginInterceptorRegistration addPathPatterns(String... patterns) { + if(type == PluginInterceptorRegistry.Type.GLOBAL){ + this.includePatterns.addAll(Arrays.asList(patterns)); + } + // 局部的 + for (String pattern : patterns) { + if(ObjectUtils.isEmpty(pattern)){ + continue; + } + this.includePatterns.add(UrlUtils.joiningUrlPath(pluginRestApiPrefix, pattern)); + } + return this; + } + + /** + * Add URL patterns to which the registered interceptor should not apply to. + * @param patterns patterns + * @return PluginInterceptorRegistration + */ + public PluginInterceptorRegistration excludePathPatterns(String... patterns) { + if(type == PluginInterceptorRegistry.Type.GLOBAL){ + this.excludePatterns.addAll(Arrays.asList(patterns)); + } + for (String pattern : patterns) { + if(ObjectUtils.isEmpty(pattern)){ + continue; + } + this.excludePatterns.add(UrlUtils.joiningUrlPath(pluginRestApiPrefix, pattern)); + } + return this; + } + + + /** + * A PathMatcher implementation to use with this interceptor. This is an optional, + * advanced property required only if using custom PathMatcher implementations + * that support mapping metadata other than the Ant path patterns supported + * by default. + * @param pathMatcher pathMatcher + * @return PluginInterceptorRegistration + */ + public PluginInterceptorRegistration pathMatcher(PathMatcher pathMatcher) { + this.pathMatcher = pathMatcher; + return this; + } + + /** + * Specify an order position to be used. Default is 0. + * @param order order + * @since 5.0 + * @return PluginInterceptorRegistration + */ + public PluginInterceptorRegistration order(int order){ + this.order = order; + return this; + } + + /** + * Return the order position to be used. + * @since 5.0 + * @return int + */ + protected int getOrder() { + return this.order; + } + + + /** + * Build the underlying interceptor. If URL patterns are provided, the returned + * type is {@link MappedInterceptor}; otherwise {@link HandlerInterceptor}. + * @return object 为 {@link MappedInterceptor} or {@link HandlerInterceptor} + */ + protected Object getInterceptor() { + if(type == PluginInterceptorRegistry.Type.PLUGIN){ + if(this.includePatterns.isEmpty()){ + this.includePatterns.add(UrlUtils.joiningUrlPath(pluginRestApiPrefix, "/**")); + } + } + if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) { + return this.interceptor; + } + + String[] include = this.includePatterns.toArray(new String[]{}); + String[] exclude = this.excludePatterns.toArray(new String[]{}); + MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor); + if (this.pathMatcher != null) { + mappedInterceptor.setPathMatcher(this.pathMatcher); + } + return mappedInterceptor; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java new file mode 100755 index 0000000..59dcade --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/interceptor/PluginInterceptorRegistry.java @@ -0,0 +1,114 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.interceptor; + +import org.springframework.core.OrderComparator; +import org.springframework.core.Ordered; +import org.springframework.web.context.request.WebRequestInterceptor; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistration; +import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 插件拦截器添加者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginInterceptorRegistry { + + + private final List registrations = new ArrayList<>(); + private final String pluginRestApiPrefix; + + public PluginInterceptorRegistry(String pluginRestApiPrefix) { + this.pluginRestApiPrefix = pluginRestApiPrefix; + } + + /** + * Adds the provided {@link HandlerInterceptor}. + * @param interceptor the interceptor to add + * @param type type {@link Type} + * @return An {@link InterceptorRegistration} that allows you optionally configure the + * registered interceptor further for example adding URL patterns it should apply to. + */ + public PluginInterceptorRegistration addInterceptor(HandlerInterceptor interceptor, Type type) { + PluginInterceptorRegistration registration = new PluginInterceptorRegistration(interceptor, + type, pluginRestApiPrefix); + this.registrations.add(registration); + return registration; + } + + /** + * Adds the provided {@link WebRequestInterceptor}. + * @param interceptor the interceptor to add + * @param type type {@link Type} + * + * @return An {@link InterceptorRegistration} that allows you optionally configure the + * registered interceptor further for example adding URL patterns it should apply to. + */ + public PluginInterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor, Type type) { + WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor); + PluginInterceptorRegistration registration = new PluginInterceptorRegistration(adapted, type, + pluginRestApiPrefix); + this.registrations.add(registration); + return registration; + } + + /** + * Return all registered interceptors. + * @return interceptors + */ + public List getInterceptors() { + return this.registrations.stream() + .sorted(INTERCEPTOR_ORDER_COMPARATOR) + .map(PluginInterceptorRegistration::getInterceptor) + .collect(Collectors.toList()); + } + + private static final Comparator INTERCEPTOR_ORDER_COMPARATOR = + OrderComparator.INSTANCE.withSourceProvider(object -> { + if (object instanceof PluginInterceptorRegistration) { + return (Ordered) ((PluginInterceptorRegistration) object)::getOrder; + } + return null; + }); + + + + public enum Type{ + /** + * 全局拦截器 + */ + GLOBAL, + + /** + * 插件局部拦截器, 必须设置 pluginRestPathPrefix 的值才生效 + */ + PLUGIN + } + + + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java new file mode 100755 index 0000000..84a98f9 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeBeanFactory.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.invoke; + +import com.gitee.starblues.annotation.Caller; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import org.springframework.beans.factory.FactoryBean; + +import java.lang.reflect.Proxy; + +/** + * 反射调用其他插件定义的接口bean工厂 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class InvokeBeanFactory implements FactoryBean { + + private Class callerInterface; + private Caller callerAnnotation; + private InvokeSupperCache invokeSupperCache; + + @Override + @SuppressWarnings("unchecked") + public T getObject() throws Exception { + ClassLoader classLoader = callerInterface.getClassLoader(); + Class[] interfaces = new Class[]{callerInterface}; + InvokeProxyHandler proxy = new InvokeProxyHandler(callerAnnotation, invokeSupperCache); + return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); + } + + @Override + public Class getObjectType() { + return callerInterface; + } + + @Override + public boolean isSingleton() { + return true; + } + + public void setCallerInterface(Class callerInterface) { + this.callerInterface = callerInterface; + } + + public void setCallerAnnotation(Caller callerAnnotation) { + this.callerAnnotation = callerAnnotation; + } + + public void setInvokeSupperCache(InvokeSupperCache invokeSupperCache) { + this.invokeSupperCache = invokeSupperCache; + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java new file mode 100755 index 0000000..ece3735 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/invoke/InvokeProxyHandler.java @@ -0,0 +1,212 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.invoke; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gitee.starblues.annotation.Caller; +import com.gitee.starblues.annotation.Supplier; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +/** + * 反射调用处理模块 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class InvokeProxyHandler implements InvocationHandler { + + private final Caller callerAnnotation; + + private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final InvokeSupperCache invokeSupperCache; + + public InvokeProxyHandler(Caller callerAnnotation, InvokeSupperCache invokeSupperCache) { + this.callerAnnotation = callerAnnotation; + this.invokeSupperCache = invokeSupperCache; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] callerArgs) throws Throwable { + String pluginId = callerAnnotation.pluginId(); + Object supplierObject = invokeSupperCache.getSupperBean(pluginId, callerAnnotation.value()); + if (supplierObject == null) { + if (ObjectUtils.isEmpty(pluginId)) { + throw new Exception("Not found '" + callerAnnotation.value() + "' supplier object"); + } else { + throw new Exception("Not found '" + callerAnnotation.value() + "' supplier object in plugin '" + + pluginId + "'"); + } + } + Caller.Method callerMethod = method.getAnnotation(Caller.Method.class); + if (callerArgs == null) { + callerArgs = new Object[]{}; + } + if (callerMethod == null) { + return notAnnotationInvoke(method, supplierObject, callerArgs); + } else { + return annotationInvoke(method, callerMethod, supplierObject, callerArgs); + } + } + + + + /** + * 有注解的调用 + * @param method 调用接口的方法 + * @param callerMethod 调用者方法注解 + * @param supplierObject 调用者对象 + * @param callerArgs 调用者参数 + * @return 返回值 + * @throws Throwable 异常 + */ + private Object annotationInvoke(Method method, Caller.Method callerMethod, + Object supplierObject, Object[] callerArgs) throws Throwable{ + + String callerMethodName = callerMethod.value(); + Class supplierClass = supplierObject.getClass(); + Method[] methods = supplierClass.getMethods(); + Method supplierMethod = null; + for (Method m : methods) { + Supplier.Method supplierMethodAnnotation = m.getAnnotation(Supplier.Method.class); + if(supplierMethodAnnotation == null){ + continue; + } + if(Objects.equals(supplierMethodAnnotation.value(), callerMethodName)){ + supplierMethod = m; + break; + } + } + if(supplierMethod == null){ + // 如果为空, 说明没有找到被调用者的注解, 则走没有注解的代理调用。 + return notAnnotationInvoke(method, supplierObject, callerArgs); + } + Class[] parameterTypes = supplierMethod.getParameterTypes(); + if(parameterTypes.length != callerArgs.length){ + // 参数不匹配 + return notAnnotationInvoke(method, supplierObject, callerArgs); + } + Object[] supplierArgs = getSupplierArgs(callerArgs, supplierMethod); + Object invokeReturn = supplierMethod.invoke(supplierObject, supplierArgs); + return getReturnObject(invokeReturn, method); + } + + /** + * 没有注解调用 + * @param method 调用接口的方法 + * @param supplierObject 提供者对象 + * @param callerArgs 调用者参数 + * @return 返回值 + * @throws Throwable 异常 + */ + private Object notAnnotationInvoke(Method method, Object supplierObject, Object[] callerArgs) throws Throwable{ + String name = method.getName(); + Class[] supplierArgClasses = new Class[callerArgs.length]; + ClassLoader classLoader = supplierObject.getClass().getClassLoader(); + for (int i = 0; i < callerArgs.length; i++) { + Object callerArg = callerArgs[i]; + try { + supplierArgClasses[i] = classLoader.loadClass(callerArg.getClass().getName()); + } catch (Exception e){ + supplierArgClasses[i] = callerArg.getClass(); + } + } + Class supplierClass = supplierObject.getClass(); + Method supplierMethod = null; + try { + supplierMethod = supplierClass.getMethod(name, supplierArgClasses); + } catch (Exception e){ + supplierMethod = findSupplierMethod(supplierClass, name, supplierArgClasses); + } + if(supplierMethod == null){ + throw ReflectionUtils.getNoSuchMethodException(supplierClass, name, supplierArgClasses); + } + Object[] supplierArgs = getSupplierArgs(callerArgs, supplierMethod); + Object invokeReturn = supplierMethod.invoke(supplierObject, supplierArgs); + return getReturnObject(invokeReturn, method); + } + + private Object[] getSupplierArgs(Object[] callerArgs, Method supplierMethod) throws Exception{ + if(callerArgs == null || callerArgs.length == 0){ + return new Class[]{}; + } + Class[] supplierParameterTypes = supplierMethod.getParameterTypes(); + Object[] supplierArgs = new Object[callerArgs.length]; + for (int i = 0; i < supplierParameterTypes.length; i++) { + Class supplierParameterType = supplierParameterTypes[i]; + Object arg = callerArgs[i]; + if(supplierParameterType.isAssignableFrom(arg.getClass())){ + // 类型相同 + supplierArgs[i] = arg; + } else { + // 类型不匹配, 尝试使用json序列化. 当前序列化针对大数据量下性能比较低, 建议将大数据量传输的参数定义到主程序中 + String json = OBJECT_MAPPER.writeValueAsString(arg); + Object serializeObject = OBJECT_MAPPER.readValue(json, supplierParameterType); + supplierArgs[i] = serializeObject; + } + } + return supplierArgs; + } + + + /** + * 得到返回值对象 + * @param invokeReturn 反射调用后返回的对象 + * @param method 调用接口的方法 + * @return 返回值对象 + * @throws Throwable Throwable + */ + private Object getReturnObject(Object invokeReturn, Method method) throws Throwable{ + if(invokeReturn == null){ + return null; + } + Class returnType = method.getReturnType(); + if(invokeReturn.getClass().isAssignableFrom(returnType)){ + return invokeReturn; + } else { + String json = OBJECT_MAPPER.writeValueAsString(invokeReturn); + return OBJECT_MAPPER.readValue(json, OBJECT_MAPPER.getTypeFactory().constructType(method.getGenericReturnType()) ); + } + } + + private Method findSupplierMethod(Class supplierClass, String methodName, Class[] supplierArgClasses){ + while (supplierClass != null){ + Method[] methods = supplierClass.getMethods(); + for (Method method : methods) { + String name = method.getName(); + if(!Objects.equals(name, methodName)){ + continue; + } + if(method.getParameterTypes().length == supplierArgClasses.length){ + return method; + } + } + supplierClass = supplierClass.getSuperclass(); + } + return null; + } + +} \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/ConfigureMainPluginEnvironment.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/ConfigureMainPluginEnvironment.java new file mode 100755 index 0000000..ae81146 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/ConfigureMainPluginEnvironment.java @@ -0,0 +1,109 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.oneself; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import com.gitee.starblues.bootstrap.annotation.OneselfConfig; +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.utils.AnnotationUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ResourceUtils; +import org.springframework.boot.env.PropertiesPropertySourceLoader; +import org.springframework.boot.env.PropertySourceLoader; +import org.springframework.boot.env.YamlPropertySourceLoader; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; + +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +/** + * 插件环境配置 + * + * @author starBlues + * @version 3.0.0 + * @since 3.0.0 + */ +public class ConfigureMainPluginEnvironment { + + private final ProcessorContext processorContext; + private final List propertySourceLoaders; + + public ConfigureMainPluginEnvironment(ProcessorContext processorContext) { + this.processorContext = processorContext; + + this.propertySourceLoaders = new ArrayList<>(2); + this.propertySourceLoaders.add(new YamlPropertySourceLoader()); + this.propertySourceLoaders.add(new PropertiesPropertySourceLoader()); + } + + public void configureEnvironment(ConfigurableEnvironment environment, String[] args) { + SpringPluginBootstrap springPluginBootstrap = processorContext.getSpringPluginBootstrap(); + OneselfConfig oneselfConfig = AnnotationUtils.findAnnotation(springPluginBootstrap.getClass(), + OneselfConfig.class); + if(oneselfConfig == null){ + return; + } + String[] mainConfigFileName = oneselfConfig.mainConfigFileName(); + if(mainConfigFileName.length == 0){ + return; + } + for (String fileName : mainConfigFileName) { + load(environment, fileName); + } + } + + + private void load(ConfigurableEnvironment environment, String fileName){ + String fileSuffix = ResourceUtils.getFileSuffix(fileName); + if(ObjectUtils.isEmpty(fileSuffix)){ + return; + } + PropertySourceLoader sourceLoader = null; + for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) { + String[] fileExtensions = propertySourceLoader.getFileExtensions(); + for (String fileExtension : fileExtensions) { + if(fileSuffix.equalsIgnoreCase(fileExtension)){ + sourceLoader = propertySourceLoader; + break; + } + } + } + if(sourceLoader == null){ + return; + } + URL url = Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource(fileName); + if(url == null){ + return; + } + try { + Path path = Paths.get(url.toURI()); + Resource resource = new FileSystemResource(path); + List> propertySources = sourceLoader.load(fileName, resource); + for (PropertySource propertySource : propertySources) { + environment.getPropertySources().addFirst(propertySource); + } + } catch (Exception e){ + throw new RuntimeException(e); + } + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/OneselfProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/OneselfProcessor.java new file mode 100755 index 0000000..0667643 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/oneself/OneselfProcessor.java @@ -0,0 +1,54 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.oneself; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.integration.operator.EmptyPluginOperator; +import com.gitee.starblues.integration.user.DefaultPluginUser; +import com.gitee.starblues.spring.extract.DefaultOpExtractFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 子启动处理器 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public class OneselfProcessor implements SpringPluginProcessor { + + @Override + public void initialize(ProcessorContext context) throws ProcessorException { + registerMainBean(context.getApplicationContext()); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.ONESELF; + } + + private void registerMainBean(GenericApplicationContext applicationContext){ + DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory(); + beanFactory.registerSingleton("extractFactory", new DefaultOpExtractFactory()); + beanFactory.registerSingleton("pluginUser", new DefaultPluginUser(applicationContext)); + beanFactory.registerSingleton("pluginOperator", new EmptyPluginOperator()); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java new file mode 100755 index 0000000..045a9c0 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/scanner/PluginClassPathBeanDefinitionScanner.java @@ -0,0 +1,42 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.scanner; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; + + +/** + * 插件自定义 classpath bean 扫描 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { + + public PluginClassPathBeanDefinitionScanner(ProcessorContext processorContext) { + this(processorContext, true); + } + + public PluginClassPathBeanDefinitionScanner(ProcessorContext processorContext, boolean useDefaultFilters) { + super(processorContext.getApplicationContext(), useDefaultFilters, + processorContext.getApplicationContext().getEnvironment(), + processorContext.getResourceLoader()); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java new file mode 100755 index 0000000..f8a5819 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginControllerProcessor.java @@ -0,0 +1,266 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.web; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.utils.AnnotationUtils; +import com.gitee.starblues.bootstrap.utils.DestroyUtils; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.utils.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.condition.RequestCondition; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; + +/** + * 插件Controller处理者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class PluginControllerProcessor implements SpringPluginProcessor { + + private final static Logger LOG = LoggerFactory.getLogger(PluginControllerProcessor.class); + + static final String PROCESS_CONTROLLERS = "PROCESS_SUCCESS"; + + + private RequestMappingHandlerMapping requestMappingHandlerMapping; + private RequestMappingHandlerAdapter handlerAdapter; + + private final AtomicBoolean canRegistered = new AtomicBoolean(false); + + + @Override + public void initialize(ProcessorContext processorContext) throws ProcessorException { + MainApplicationContext mainApplicationContext = processorContext.getMainApplicationContext(); + this.requestMappingHandlerMapping = mainApplicationContext.getRequestMappingHandlerMapping(); + this.handlerAdapter = mainApplicationContext.getRequestMappingHandlerAdapter(); + canRegistered.set(true); + } + + + @Override + public void refreshBefore(ProcessorContext processorContext) throws ProcessorException { + if(!canRegistered.get()){ + return; + } + GenericApplicationContext applicationContext = processorContext.getApplicationContext(); + applicationContext.getDefaultListableBeanFactory() + .addBeanPostProcessor(new ControllerPostProcessor(processorContext)); + } + + @Override + public void refreshAfter(ProcessorContext processorContext) throws ProcessorException { + if(!canRegistered.get()){ + return; + } + String pluginId = processorContext.getPluginDescriptor().getPluginId(); + List controllerWrappers = processorContext.getRegistryInfo(PROCESS_CONTROLLERS); + if(ObjectUtils.isEmpty(controllerWrappers)){ + return; + } + GenericApplicationContext applicationContext = processorContext.getApplicationContext(); + + Iterator iterator = controllerWrappers.iterator(); + String pathPrefix = PluginConfigUtils.getPluginRestPrefix(processorContext.getConfiguration(), pluginId); + PluginRequestMappingHandlerMapping pluginHandlerMapping = new PluginRequestMappingHandlerMapping(pathPrefix); + + while (iterator.hasNext()){ + ControllerWrapper controllerWrapper = iterator.next(); + if(!applicationContext.containsBean(controllerWrapper.getBeanName())){ + iterator.remove(); + } + Object controllerBean = applicationContext.getBean(controllerWrapper.getBeanName()); + pluginHandlerMapping.registerHandler(controllerBean); + List registerMappingInfo = pluginHandlerMapping.getAndClear(); + + Set requestMappingInfoSet = new HashSet<>(registerMappingInfo.size()); + for (RegisterMappingInfo mappingInfo : registerMappingInfo) { + RequestMappingInfo requestMappingInfo = mappingInfo.getRequestMappingInfo(); + requestMappingHandlerMapping.registerMapping( + requestMappingInfo, + mappingInfo.getHandler(), + mappingInfo.getMethod() + ); + LOG.info("插件[{}]注册接口: {}", pluginId, requestMappingInfo); + requestMappingInfoSet.add(requestMappingInfo); + } + controllerWrapper.setRequestMappingInfo(requestMappingInfoSet); + } + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + List controllerWrappers = context.getRegistryInfo(PROCESS_CONTROLLERS); + if(ObjectUtils.isEmpty(controllerWrappers)){ + return; + } + for (ControllerWrapper controllerWrapper : controllerWrappers) { + unregister(controllerWrapper); + } + controllerWrappers.clear(); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } + + /** + * 卸载具体的Controller操作 + * @param controllerBeanWrapper controllerBean包装 + */ + private void unregister(ControllerWrapper controllerBeanWrapper) { + Set requestMappingInfoSet = controllerBeanWrapper.getRequestMappingInfo(); + if(requestMappingInfoSet != null && !requestMappingInfoSet.isEmpty()){ + for (RequestMappingInfo requestMappingInfo : requestMappingInfoSet) { + requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); + } + } + if(handlerAdapter != null){ + Class beanClass = controllerBeanWrapper.getBeanClass(); + DestroyUtils.destroyValue(handlerAdapter, "sessionAttributesHandlerCache", beanClass); + DestroyUtils.destroyValue(handlerAdapter, "initBinderCache", beanClass); + DestroyUtils.destroyValue(handlerAdapter, "modelAttributeCache", beanClass); + } + } + + private static class ControllerPostProcessor implements BeanPostProcessor { + + private final static Logger LOG = LoggerFactory.getLogger(ControllerPostProcessor.class); + + private final ProcessorContext processorContext; + + private ControllerPostProcessor(ProcessorContext processorContext) { + this.processorContext = processorContext; + } + + @SuppressWarnings("unchecked") + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + Class aClass = bean.getClass(); + RequestMapping requestMapping = AnnotationUtils.findAnnotation(aClass, RequestMapping.class); + boolean isController = AnnotationUtils.existOr(aClass, new Class[]{ + Controller.class, RestController.class + }); + if(requestMapping != null && isController){ + addControllerWrapper(beanName, aClass); + } + return bean; + } + + private void addControllerWrapper(String beanName, Class aClass){ + List controllerWrappers = processorContext.getRegistryInfo(PROCESS_CONTROLLERS); + if(controllerWrappers == null){ + controllerWrappers = new ArrayList<>(); + processorContext.addRegistryInfo(PROCESS_CONTROLLERS, controllerWrappers); + } + ControllerWrapper controllerWrapper = new ControllerWrapper(); + controllerWrapper.setBeanName(beanName); + controllerWrapper.setBeanClass(aClass); + controllerWrappers.add(controllerWrapper); + } + + } + + @Data + static class ControllerWrapper{ + + /** + * controller bean 名称 + */ + private String beanName; + + /** + * controller bean 类型 + */ + private Class beanClass; + + /** + * controller 的 RequestMappingInfo 集合 + */ + private Set requestMappingInfo; + } + + + private static class PluginRequestMappingHandlerMapping extends RequestMappingHandlerMapping { + + private final List registerMappingInfo = new ArrayList<>(); + + public PluginRequestMappingHandlerMapping(){ + this(null); + } + + public PluginRequestMappingHandlerMapping(String pathPrefix){ + if(!ObjectUtils.isEmpty(pathPrefix)){ + Map>> prefixes = new HashMap<>(); + prefixes.put(pathPrefix, c->true); + setPathPrefixes(prefixes); + } + } + + public void registerHandler(Object handler){ + detectHandlerMethods(handler); + } + + @Override + protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { + super.registerHandlerMethod(handler, method, mapping); + registerMappingInfo.add(new RegisterMappingInfo(handler, method, mapping)); + } + + public List getAndClear(){ + List registerMappingInfo = new ArrayList<>(this.registerMappingInfo); + this.registerMappingInfo.clear(); + return registerMappingInfo; + } + + } + + @AllArgsConstructor + @Getter + private static class RegisterMappingInfo{ + private final Object handler; + private final Method method; + private final RequestMappingInfo requestMappingInfo; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java new file mode 100755 index 0000000..22bc9d3 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginInterceptorsProcessor.java @@ -0,0 +1,164 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.web; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.bootstrap.processor.interceptor.PluginInterceptorRegister; +import com.gitee.starblues.bootstrap.processor.interceptor.PluginInterceptorRegistry; +import com.gitee.starblues.bootstrap.utils.SpringBeanUtils; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.utils.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.web.context.request.WebRequestInterceptor; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.handler.AbstractHandlerMapping; +import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 插件拦截器处理者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginInterceptorsProcessor implements SpringPluginProcessor { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final static String INTERCEPTORS = "pluginHandlerInterceptors"; + + private List handlerMappings; + + + @Override + public void initialize(ProcessorContext context) throws ProcessorException { + MainApplicationContext applicationContext = context.getMainApplicationContext(); + handlerMappings = SpringBeanCustomUtils.getBeans(applicationContext, + AbstractHandlerMapping.class); + if(handlerMappings.isEmpty()){ + logger.warn("Not found AbstractHandlerMapping, Plugin interceptor can't use"); + } + } + + @Override + public void refreshAfter(ProcessorContext context) throws ProcessorException { + if(handlerMappings.isEmpty()){ + return; + } + List interceptorRegisters = SpringBeanUtils.getBeans( + context.getApplicationContext(), + PluginInterceptorRegister.class); + + Map> handlerInterceptorMap = new HashMap<>(); + for (AbstractHandlerMapping handlerMapping : handlerMappings) { + List adaptedInterceptors = getAdaptedInterceptors(handlerMapping); + if(!ObjectUtils.isEmpty(adaptedInterceptors)){ + handlerInterceptorMap.put(handlerMapping, adaptedInterceptors); + } + } + if(handlerInterceptorMap.isEmpty()){ + logger.debug("handlerInterceptorMap is empty"); + return; + } + IntegrationConfiguration configuration = context.getConfiguration(); + String pluginId = context.getPluginDescriptor().getPluginId(); + String pluginRestPrefix = PluginConfigUtils.getPluginRestPrefix(configuration, pluginId); + + + List storeInterceptors = new ArrayList<>(); + for (PluginInterceptorRegister interceptorRegister : interceptorRegisters) { + PluginInterceptorRegistry interceptorRegistry = new PluginInterceptorRegistry(pluginRestPrefix); + interceptorRegister.registry(interceptorRegistry); + List registryInterceptors = interceptorRegistry.getInterceptors(); + if(registryInterceptors == null || registryInterceptors.isEmpty()){ + continue; + } + for (Object interceptor : registryInterceptors) { + HandlerInterceptor handlerInterceptor = adaptInterceptor(interceptor); + for (List value : handlerInterceptorMap.values()) { + value.add(handlerInterceptor); + } + storeInterceptors.add(handlerInterceptor); + } + } + context.addRegistryInfo(INTERCEPTORS, storeInterceptors); + } + + @Override + public void close(ProcessorContext context) throws ProcessorException { + if(handlerMappings.isEmpty()){ + return; + } + List storeInterceptors = context.getRegistryInfo(INTERCEPTORS); + if(ObjectUtils.isEmpty(storeInterceptors)){ + return; + } + for (HandlerInterceptor storeInterceptor : storeInterceptors) { + for (AbstractHandlerMapping handlerMapping : handlerMappings) { + List adaptedInterceptors = getAdaptedInterceptors(handlerMapping); + if(!ObjectUtils.isEmpty(adaptedInterceptors)){ + adaptedInterceptors.remove(storeInterceptor); + } + } + } + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } + + /** + * 得到拦截器存储者 + * @param handlerMapping AbstractHandlerMapping + * @return List + */ + private List getAdaptedInterceptors(AbstractHandlerMapping handlerMapping){ + try { + return ClassUtils.getReflectionField(handlerMapping, "adaptedInterceptors", List.class); + } catch (IllegalAccessException e) { + logger.error("Can't get 'adaptedInterceptors' from AbstractHandlerMapping, so " + + "You can't use HandlerInterceptor. {} ", e.getMessage()); + return null; + } + } + + /** + * 转换拦截器 + * @param interceptor interceptor + * @return HandlerInterceptor + */ + private HandlerInterceptor adaptInterceptor(Object interceptor) { + if (interceptor instanceof HandlerInterceptor) { + return (HandlerInterceptor) interceptor; + } else if (interceptor instanceof WebRequestInterceptor) { + return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); + } else { + throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); + } + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java new file mode 100755 index 0000000..40e30a5 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/processor/web/PluginStaticResourceProcessor.java @@ -0,0 +1,74 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.processor.web; + +import com.gitee.starblues.bootstrap.processor.ProcessorContext; +import com.gitee.starblues.bootstrap.processor.ProcessorException; +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.util.HashSet; +import java.util.Set; + +/** + * 插件web资源处理器. 获取资源配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginStaticResourceProcessor implements SpringPluginProcessor { + + + /** + * 静态文件配置前缀 + * 静态文件路径 + * classpath: static/ + * file: D://path/test + */ + private final static String STATIC_LOCATIONS = "spring.resources.static-locations"; + + + @Override + public void refreshBefore(ProcessorContext context) throws ProcessorException { + GenericApplicationContext applicationContext = context.getApplicationContext(); + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + String property = environment.getProperty(STATIC_LOCATIONS); + if(ObjectUtils.isEmpty(property)){ + return; + } + String[] staticLocations = property.split(","); + if (ObjectUtils.isEmpty(staticLocations)) { + return; + } + Set staticLocationsSet = new HashSet<>(staticLocations.length); + for (String staticLocation : staticLocations) { + if(ObjectUtils.isEmpty(staticLocation)){ + continue; + } + staticLocationsSet.add(staticLocation); + } + context.getWebConfig().setResourceLocations(staticLocationsSet); + } + + @Override + public ProcessorContext.RunMode runMode() { + return ProcessorContext.RunMode.PLUGIN; + } +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/AutowiredTypeDefiner.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/AutowiredTypeDefiner.java new file mode 100755 index 0000000..7926a3d --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/AutowiredTypeDefiner.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.realize; + +import com.gitee.starblues.bootstrap.AutowiredTypeDefinerConfig; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.gitee.starblues.utils.UrlUtils; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.util.HashSet; +import java.util.Set; + +/** + * autowiredType 批量定义接口 + * + * @author starBlues + * @since 3.0.3 + * @version 3.0.3 + */ +public interface AutowiredTypeDefiner { + + /** + * 定义 ClassDefiner + * @param config 往config中进行配置 ClassDefiner + */ + void config(AutowiredTypeDefinerConfig config); + + + @Getter + @EqualsAndHashCode + class ClassDefiner{ + /** + * 注入类型 + */ + private final AutowiredType.Type autowiredType; + + /** + * 类名称匹配 + */ + private final Set classNamePatterns; + + private ClassDefiner(AutowiredType.Type autowiredType, Set classNamePatterns){ + this.autowiredType = autowiredType; + this.classNamePatterns = classNamePatterns; + } + + public static ClassDefiner config(AutowiredType.Type autowiredType, Class... classes){ + if(autowiredType == null){ + throw new IllegalArgumentException("autowiredType 参数不能为空"); + } + int length = classes.length; + if(length == 0){ + throw new IllegalArgumentException("classes 参数不能为空"); + } + Set classNamePatterns = new HashSet<>(length); + for (Class aClass : classes) { + classNamePatterns.add(UrlUtils.formatMatchUrl(aClass.getName())); + } + return new ClassDefiner(autowiredType, classNamePatterns); + } + + public static ClassDefiner config(AutowiredType.Type autowiredType, String... classNamePatterns){ + if(autowiredType == null){ + throw new IllegalArgumentException("autowiredType 参数不能为空"); + } + int length = classNamePatterns.length; + if(length == 0){ + throw new IllegalArgumentException("classNamePatterns 参数不能为空"); + } + Set classNamePatternsSet = new HashSet<>(length); + for (String classNamePattern : classNamePatterns) { + classNamePatternsSet.add(UrlUtils.formatMatchUrl(classNamePattern)); + } + return new ClassDefiner(autowiredType, classNamePatternsSet); + } + + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java new file mode 100755 index 0000000..b3401cd --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/PluginCloseListener.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.realize; + +import com.gitee.starblues.core.PluginCloseType; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 插件被停止监听者。用于自定义关闭资源 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public interface PluginCloseListener { + + /** + * 关闭时调用 + * @param descriptor 当前插件描述者 + * @deprecated 在 3.1.1 版本会被删除 + * @since 3.0.0 + */ + default void close(PluginDescriptor descriptor){} + + /** + * 关闭时调用 + * @param applicationContext 当前插件的ApplicationContext + * @param pluginInfo 当前插件信息 + * @param closeType 停止类型 + * @since 3.1.0 + */ + default void close(GenericApplicationContext applicationContext, + PluginInfo pluginInfo, PluginCloseType closeType){ + close(pluginInfo != null ? pluginInfo.getPluginDescriptor() : null); + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java new file mode 100755 index 0000000..2946e1d --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/realize/StopValidator.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.realize; + +/** + * 插件停止校验器. 自主实现判断是否可卸载 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public interface StopValidator { + + + /** + * 校验是否可停止/卸载。如果校验器抛出异常. 默认插件不可停止/卸载 + * @return 校验结果 + * @since 3.0.0 + */ + Result verify(); + + + class Result{ + /** + * 是否可卸载 + */ + private final boolean verify; + + /** + * 不可卸载时的提示内容 + */ + private String message; + + + public Result(boolean verify) { + this.verify = verify; + } + + public Result(boolean verify, String message) { + this.verify = verify; + this.message = message; + } + + /** + * 可卸载 + * @return Result + * @since 3.0.3 + */ + public static Result ok(){ + return new Result(true); + } + + /** + * 禁止停止 + * @return Result + * @since 3.0.3 + */ + public static Result forbid(){ + return forbid(null); + } + + /** + * 禁止停止 + * @param message 禁止停止信息 + * @return Result + * @since 3.0.3 + */ + public static Result forbid(String message){ + return new Result(false, message); + } + + public boolean isVerify() { + return verify; + } + + public String getMessage() { + return message; + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java new file mode 100755 index 0000000..23a579a --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/AnnotationUtils.java @@ -0,0 +1,64 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.utils; + + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * 注解工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class AnnotationUtils { + + + private AnnotationUtils(){} + + public static A findAnnotation(Method method, Class annotationType) { + return org.springframework.core.annotation.AnnotationUtils.findAnnotation(method, annotationType); + } + + + public static A findAnnotation(Class clazz, Class annotationType) { + return org.springframework.core.annotation.AnnotationUtils.findAnnotation(clazz, annotationType); + } + + public static boolean existOr(Class clazz, Class[] annotationTypes) { + for (Class annotationType : annotationTypes) { + A annotation = findAnnotation(clazz, annotationType); + if(annotation != null){ + return true; + } + } + return false; + } + + public static boolean existAnd(Class clazz, Class[] annotationTypes) { + for (Class annotationType : annotationTypes) { + A annotation = findAnnotation(clazz, annotationType); + if(annotation == null){ + return false; + } + } + return true; + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java new file mode 100755 index 0000000..8b7763b --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/DestroyUtils.java @@ -0,0 +1,103 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.utils; + +import com.gitee.starblues.utils.ReflectionUtils; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * 销毁工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DestroyUtils { + + private DestroyUtils(){} + + public static void destroyAll(Object object, String fieldName, Class fieldType){ + destroyAll(object, object.getClass(), fieldName, fieldType); + } + + public static void destroyAll(Object object, Class objectClass, String fieldName, Class fieldType){ + try { + Object fieldObject = ReflectionUtils.getField(object, objectClass, fieldName, fieldType); + if(fieldObject == null){ + return; + } + destroyAll(fieldObject); + } catch (Exception e){ + // 忽略 + } + } + + public static void destroyValue(Object object, String fieldName, Object value){ + destroyValue(object, object.getClass(), fieldName, value); + } + + public static void destroyValue(Object object, Class objectClass, String fieldName, Object value){ + destroyValue(object, objectClass, fieldName, null, value); + } + + + public static void destroyValue(Object object, Class objectClass, String fieldName, Class fieldType, Object value){ + try { + Object fieldObject = ReflectionUtils.getField(object, objectClass, fieldName, fieldType); + if(fieldObject == null){ + return; + } + destroyValue(fieldObject, value); + } catch (Exception e){ + // 忽略 + } + } + + @SuppressWarnings("all") + public static void destroyAll(Object object){ + if(object == null){ + return; + } + if(object instanceof Map){ + ((Map)object).clear(); + } else if (object instanceof Collection){ + ((Collection)object).clear(); + } else if(object.getClass().isArray()){ + Object[] array = (Object[])object; + for (int i = 0; i < array.length; i++) { + array[i] = null; + } + } + } + + @SuppressWarnings("all") + public static void destroyValue(Object object, Object value){ + if(object == null){ + return; + } + if(object instanceof Map){ + ((Map)object).remove(value); + } else if (object instanceof Collection){ + ((Collection) object).removeIf(next -> Objects.equals(next, value)); + } + } + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java new file mode 100755 index 0000000..9fb4367 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/ProcessorUtils.java @@ -0,0 +1,43 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.utils; + +import com.gitee.starblues.bootstrap.processor.SpringPluginProcessor; + +import java.util.List; +import java.util.function.Supplier; + +/** + * ProcessorUtils + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ProcessorUtils { + + public static void add(List pluginProcessors, Supplier supplier){ + try { + SpringPluginProcessor pluginProcessor = supplier.get(); + pluginProcessors.add(pluginProcessor); + } catch (Throwable e){ + // 忽略 + } + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java new file mode 100755 index 0000000..54b4c7c --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/java/com/gitee/starblues/bootstrap/utils/SpringBeanUtils.java @@ -0,0 +1,106 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.bootstrap.utils; + +import org.springframework.context.ApplicationContext; +import org.springframework.util.ClassUtils; + +import java.lang.annotation.Annotation; +import java.util.*; + +/** + * 插件bean工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class SpringBeanUtils { + /** + * 获取bean名称 + * @param applicationContext ApplicationContext + * @return bean名称集合 + */ + public static Set getBeanName(ApplicationContext applicationContext){ + String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); + Set set = new HashSet<>(beanDefinitionNames.length); + set.addAll(Arrays.asList(beanDefinitionNames)); + return set; + } + + /** + * 得到ApplicationContext中的bean的实现 + * @param applicationContext ApplicationContext + * @param aClass 接口或者抽象类型bean类型 + * @param 接口或者抽象类型bean类型 + * @return 所有的实现对象 + */ + public static List getBeans(ApplicationContext applicationContext, Class aClass) { + Map beansOfTypeMap = applicationContext.getBeansOfType(aClass); + if(beansOfTypeMap.isEmpty()){ + return new ArrayList<>(); + } + return new ArrayList<>(beansOfTypeMap.values()); + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext ApplicationContext容器 + * @param aClass bean 类型 + * @param bean 类型 + * @return 存在bean对象, 不存在返回null + */ + public static T getExistBean(ApplicationContext applicationContext, Class aClass){ + String[] beanNamesForType = applicationContext.getBeanNamesForType(aClass, false, false); + if(beanNamesForType.length > 0){ + return applicationContext.getBean(aClass); + } else { + return null; + } + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext ApplicationContext容器 + * @param beanName bean 名称 + * @param 返回的bean类型 + * @return 存在bean对象, 不存在返回null + */ + @SuppressWarnings("unchecked") + public static T getExistBean(ApplicationContext applicationContext, String beanName){ + if(applicationContext.containsBean(beanName)){ + Object bean = applicationContext.getBean(beanName); + return (T) bean; + } else { + return null; + } + } + + /** + * 通过注解获取bean + * @param applicationContext applicationContext + * @param annotationType 注解类型 + * @return List + */ + public static List getBeansWithAnnotation(ApplicationContext applicationContext, + Class annotationType){ + Map beanMap = applicationContext.getBeansWithAnnotation(annotationType); + return new ArrayList<>(beanMap.values()); + } + + +} diff --git a/iot-spring-brick/spring-brick-bootstrap/src/main/resources/META-INF/spring.factories b/iot-spring-brick/spring-brick-bootstrap/src/main/resources/META-INF/spring.factories new file mode 100755 index 0000000..9d17993 --- /dev/null +++ b/iot-spring-brick/spring-brick-bootstrap/src/main/resources/META-INF/spring.factories @@ -0,0 +1,7 @@ +# Auto Configuration Import Filters +org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ +com.gitee.starblues.bootstrap.PluginDisableAutoConfiguration + +# Auto Configure +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.gitee.starblues.bootstrap.ResolveHttpMessageConvertersConfiguration \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-common/pom.xml b/iot-spring-brick/spring-brick-common/pom.xml new file mode 100755 index 0000000..de7dd40 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + + com.gitee.starblues + iot-spring-brick + 3.1.4 + + + spring-brick-common + 框架公共模块 + jar + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java new file mode 100755 index 0000000..f5d4391 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/AbstractDependencyPlugin.java @@ -0,0 +1,108 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +/** + * 依赖的插件 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class AbstractDependencyPlugin implements DependencyPlugin{ + + public static final String SPLIT_ALL = ","; + public static final String SPLIT_ONE = "@"; + + + /** + * set依赖插件id + * + * @param id 插件id + */ + public abstract void setId(String id); + + /** + * set依赖插件版本 + * + * @param version 插件版本 + */ + public abstract void setVersion(String version); + + /** + * set optional + * + * @param optional 是否可选 + */ + public abstract void setOptional(Boolean optional); + + + public static String toStr(List dependencyPlugins){ + if(dependencyPlugins == null || dependencyPlugins.isEmpty()){ + return null; + } + StringBuilder stringBuilder = new StringBuilder(); + final int size = dependencyPlugins.size(); + for (int i = 0; i < size; i++) { + AbstractDependencyPlugin dependencyPlugin = dependencyPlugins.get(i); + Boolean optional = dependencyPlugin.getOptional(); + if(optional == null){ + optional = false; + } + stringBuilder.append(dependencyPlugin.getId()) + .append(SPLIT_ONE).append(dependencyPlugin.getVersion()) + .append(SPLIT_ONE).append(optional); + if(i <= size - 2){ + stringBuilder.append(SPLIT_ALL); + } + } + return stringBuilder.toString(); + } + + + public static List toList(String str, Supplier supplier){ + if(str == null || "".equals(str)){ + return Collections.emptyList(); + } + String[] all = str.split(SPLIT_ALL); + if(all.length == 0){ + return Collections.emptyList(); + } + List list = new ArrayList<>(all.length); + for (String s : all) { + String[] one = s.split(SPLIT_ONE); + if(one.length == 0){ + continue; + } + if(one.length != 3){ + continue; + } + AbstractDependencyPlugin dependencyPlugin = supplier.get(); + dependencyPlugin.setId(one[0]); + dependencyPlugin.setVersion(one[1]); + dependencyPlugin.setOptional("true".equalsIgnoreCase(one[2])); + list.add(dependencyPlugin); + } + return list; + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java new file mode 100755 index 0000000..e545c01 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/Constants.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + + +/** + * 静态常量 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class Constants { + + private Constants(){} + + + /** + * 禁用所有插件标志 + */ + public final static String DISABLED_ALL_PLUGIN = "*"; + + /** + * 允许所有版本的标志 + */ + public final static String ALLOW_VERSION = "0.0.0"; + + /** + * 加载到主程序依赖的标志 + */ + public final static String LOAD_TO_MAIN_SIGN = "@LOAD_TO_MAIN"; + + /** + * 相对路径符号标志 + */ + public final static String RELATIVE_SIGN = "~"; + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java new file mode 100755 index 0000000..0fae35c --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/DependencyPlugin.java @@ -0,0 +1,49 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + +/** + * 依赖的插件 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface DependencyPlugin { + + /** + * 依赖插件id + * + * @return String + */ + String getId(); + + /** + * 依赖插件版本. 如果设置为: 0.0.0 表示支持任意版本依赖 + * + * @return String + */ + String getVersion(); + + /** + * 是否为必须依赖. 默认: false + * + * @return boolean + */ + Boolean getOptional(); + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java new file mode 100755 index 0000000..74b6267 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/ManifestKey.java @@ -0,0 +1,120 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + +import java.util.jar.Attributes; + +/** + * Manifest-Key + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class ManifestKey { + + /** + * Manifest version + */ + public static final String MANIFEST_VERSION = "Manifest-Version"; + + /** + * Manifest Build Time + */ + public static final String BUILD_TIME = "Build-Time"; + + /** + * Manifest-version: 1.0 + */ + public static final String MANIFEST_VERSION_1_0 = "1.0"; + + /** + * plugin meta path + */ + public static final String PLUGIN_META_PATH = "Plugin-Meta-Path"; + + /** + * plugin package type + */ + public static final String PLUGIN_PACKAGE_TYPE = "Plugin-Package-Type"; + + + /** + * main class + */ + public static final String MAIN_CLASS = "Main-Class"; + + /** + * jar in jar: main class value + */ + public static final String MAIN_CLASS_VALUE = "com.gitee.starblues.loader.launcher.SpringMainProdBootstrap"; + + /** + * jar in jar: start class + */ + public static final String START_CLASS = "Start-Class"; + + /** + * jar class path + */ + public static final String CLASS_PATH = "Class-Path"; + + /** + * jar main lib path + */ + public static final String MAIN_LIB_DIR = "Lib-Dir"; + + + /** + * main package type + */ + public static final String MAIN_PACKAGE_TYPE = "Main-Package-Type"; + + /** + * jar main development mode + */ + public static final String DEVELOPMENT_MODE = "Development-Mode"; + + /** + * jar package name + */ + public static final String IMPLEMENTATION_TITLE = "Implementation-Title"; + + /** + * jar package version + */ + public static final String IMPLEMENTATION_VERSION = "Implementation-Version"; + + + /** + * 获取值 + * + * @param attributes attributes + * @param key 获取的key + * @return 获取的值 + */ + public static String getValue(Attributes attributes, String key){ + try { + return attributes.getValue(key); + } catch (Throwable e){ + // 忽略 + return null; + } + } + + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java new file mode 100755 index 0000000..d1fbdaa --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageStructure.java @@ -0,0 +1,91 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; + +/** + * 插件打包结构 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class PackageStructure { + + private PackageStructure(){} + + public static final String[] ILLEGAL_FORMAT = new String[]{ + AbstractDependencyPlugin.SPLIT_ALL, AbstractDependencyPlugin.SPLIT_ONE + }; + + + public static final String CHARSET_NAME = "utf-8"; + + public static final String SEPARATOR = "/"; + + public static final String CLASSES_NAME = "classes"; + + public static final String LIB_NAME = "lib"; + + public static final String META_INF_NAME = "META-INF"; + + public static final String MANIFEST = "MANIFEST.MF"; + + public static final String PLUGIN_META_NAME = "PLUGIN.META"; + + public static final String RESOURCES_DEFINE_NAME = "RESOURCES.CONF"; + + public static final String RESOURCES_DEFINE_DEPENDENCIES = "dependencies.index"; + public static final String RESOURCES_DEFINE_LOAD_MAIN_INCLUDES = "load.main.resources.includes"; + public static final String RESOURCES_DEFINE_LOAD_MAIN_EXCLUDES = "load.main.resources.excludes"; + + public static final String PROD_MANIFEST_PATH = META_INF_NAME + SEPARATOR + MANIFEST; + + public static final String PROD_RESOURCES_DEFINE_PATH = META_INF_NAME + SEPARATOR + RESOURCES_DEFINE_NAME; + + public static final String PROD_PLUGIN_META_PATH = META_INF_NAME + SEPARATOR + PLUGIN_META_NAME; + + public static final String PROD_CLASSES_PATH = CLASSES_NAME + SEPARATOR; + + public static final String PROD_LIB_PATH = LIB_NAME + SEPARATOR; + + + public static String resolvePath(String path){ + if(path == null || "".equals(path)){ + return path; + } + if(path.contains(SEPARATOR)){ + return path.replaceAll(SEPARATOR, Matcher.quoteReplacement(File.separator)); + } + return path; + } + + public static String getIllegal(String str){ + for (String s : ILLEGAL_FORMAT) { + if(str.contains(s)){ + return s; + } + } + return null; + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java new file mode 100755 index 0000000..66c5ada --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PackageType.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + +/** + * 打包类型 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PackageType { + + public static final String PLUGIN_PACKAGE_TYPE_DEV = "dev"; + + public static final String PLUGIN_PACKAGE_TYPE_JAR = "jar"; + public static final String PLUGIN_PACKAGE_TYPE_JAR_OUTER = "jar-outer"; + public static final String PLUGIN_PACKAGE_TYPE_ZIP = "zip"; + public static final String PLUGIN_PACKAGE_TYPE_ZIP_OUTER = "zip-outer"; + public static final String PLUGIN_PACKAGE_TYPE_DIR = "dir"; + + public static final String MAIN_PACKAGE_TYPE_JAR = "jar"; + public static final String MAIN_PACKAGE_TYPE_JAR_OUTER = "jar-outer"; + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java new file mode 100755 index 0000000..106e210 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDescriptorKey.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common; + +/** + * 插件描述文件配置key + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginDescriptorKey { + + + /** Must configure prop **/ + public static final String PLUGIN_ID = "plugin.id"; + public static final String PLUGIN_BOOTSTRAP_CLASS = "plugin.bootstrapClass"; + public static final String PLUGIN_VERSION = "plugin.version"; + + /** Optional configure prop **/ + public static final String PLUGIN_DESCRIPTION = "plugin.description"; + public static final String PLUGIN_PROVIDER = "plugin.provider"; + public static final String PLUGIN_DEPENDENCIES = "plugin.dependencies"; + public static final String PLUGIN_REQUIRES = "plugin.requires"; + public static final String PLUGIN_LICENSE = "plugin.license"; + public static final String PLUGIN_CONFIG_FILE_NAME = "plugin.configFileName"; + public static final String PLUGIN_CONFIG_FILE_LOCATION = "plugin.configFileLocation"; + public static final String PLUGIN_ARGS = "plugin.args"; + public static final String PLUGIN_LIB_DIR = "plugin.libDir"; + + /** System create prop **/ + public static final String PLUGIN_PATH = "plugin.system.path"; + public static final String PLUGIN_RESOURCES_CONFIG = "plugin.system.resourcesConfig"; + + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDisableAutoConfig.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDisableAutoConfig.java new file mode 100755 index 0000000..24bdefe --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/PluginDisableAutoConfig.java @@ -0,0 +1,25 @@ +package com.gitee.starblues.common; + +import java.util.HashSet; +import java.util.Set; + +/** + * 插件禁用的 AutoConfiguration 配置 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public class PluginDisableAutoConfig { + + private final static Set COMMON_PLUGIN_DISABLE_AUTO_CONFIG = new HashSet<>(); + + + static { + COMMON_PLUGIN_DISABLE_AUTO_CONFIG.add("com.gitee.starblues.integration.SpringBootPluginStarter"); + } + + public static Set getCommonPluginDisableAutoConfig() { + return COMMON_PLUGIN_DISABLE_AUTO_CONFIG; + } +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AbstractPluginCipher.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AbstractPluginCipher.java new file mode 100755 index 0000000..9233c33 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AbstractPluginCipher.java @@ -0,0 +1,58 @@ +package com.gitee.starblues.common.cipher; + +import com.gitee.starblues.utils.MapValueGetter; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.Map; + +/** + * 抽象的插件解密 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public abstract class AbstractPluginCipher implements PluginCipher{ + + protected MapValueGetter parameters; + + protected AbstractPluginCipher(){ + } + + public void initParams(Map params){ + parameters = new MapValueGetter(params); + } + + @Override + public String encrypt(String sourceStr) throws Exception { + if(ObjectUtils.isEmpty(sourceStr)){ + return ""; + } + return encryptImpl(sourceStr); + } + + /** + * 加密实现 + * @param sourceStr 原始字符串 + * @return 加密后的字节 + * @throws Exception 加密异常 + */ + protected abstract String encryptImpl(String sourceStr) throws Exception; + + + @Override + public String decrypt(String cryptoStr) throws Exception { + if(ObjectUtils.isEmpty(cryptoStr)){ + return ""; + } + return decryptImpl(cryptoStr); + } + + /** + * 解密实现 + * @param cryptoStr 解密字符串 + * @return 解密后的字符 + * @throws Exception 解密异常 + */ + protected abstract String decryptImpl(String cryptoStr) throws Exception; +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AesPluginCipher.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AesPluginCipher.java new file mode 100755 index 0000000..76d6c8d --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/AesPluginCipher.java @@ -0,0 +1,83 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common.cipher; + +import com.gitee.starblues.utils.Assert; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +/** + * AES 加密 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public class AesPluginCipher extends AbstractPluginCipher{ + + public final static String SECRET_KEY = "secretKey"; + + private static final String INSTANCE_KEY = "AES/ECB/PKCS5Padding"; + private static final String AES_KEY = "AES"; + + @Override + protected String encryptImpl(String sourceStr) throws Exception { + Key convertSecretKey = getKey(); + Cipher cipher = Cipher.getInstance(INSTANCE_KEY); + cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey); + byte[] result = cipher.doFinal(sourceStr.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(result); + } + + @Override + protected String decryptImpl(String cryptoStr) throws Exception { + Key convertSecretKey = getKey(); + Cipher cipher = Cipher.getInstance(INSTANCE_KEY); + cipher.init(Cipher.DECRYPT_MODE, convertSecretKey); + byte[] decode = Base64.getDecoder().decode(cryptoStr); + byte[] result = cipher.doFinal(decode); + return new String(result, StandardCharsets.UTF_8); + } + + + private Key getKey() throws Exception{ + String secretKey = super.parameters.getString(SECRET_KEY); + Assert.isNotEmpty(secretKey, SECRET_KEY + " 不能为空"); + byte[] keyBytes = Base64.getDecoder().decode(secretKey); + return new SecretKeySpec(keyBytes, AES_KEY); + } + + /** + * 获取秘钥 + * @return 秘钥字符串 + * @throws NoSuchAlgorithmException NoSuchAlgorithmException + */ + public static String generateKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_KEY); + keyGenerator.init(128); + Key secretKey = keyGenerator.generateKey(); + byte[] keyBytes = secretKey.getEncoded(); + return Base64.getEncoder().encodeToString(keyBytes); + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/PluginCipher.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/PluginCipher.java new file mode 100755 index 0000000..b6abc4e --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/PluginCipher.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common.cipher; + +/** + * 插件密码接口 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public interface PluginCipher { + + /** + * 加密 + * @param sourceStr 原始字符 + * @return 加密后的字节 + * @throws Exception 加密异常 + */ + String encrypt(String sourceStr) throws Exception; + + /** + * 解密 + * @param cryptoStr 加密的字符 + * @return 解密后的字符 + * @throws Exception 解密异常 + */ + String decrypt(String cryptoStr) throws Exception; + + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/RsaPluginCipher.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/RsaPluginCipher.java new file mode 100755 index 0000000..1c88129 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/common/cipher/RsaPluginCipher.java @@ -0,0 +1,107 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.common.cipher; + +import com.gitee.starblues.utils.Assert; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +/** + * 非对称插件加解密 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public class RsaPluginCipher extends AbstractPluginCipher{ + + public final static String PUBLIC_KEY = "publicKey"; + public final static String PRIVATE_KEY = "privateKey"; + + public RsaPluginCipher(){ + } + + @Override + protected String encryptImpl(String sourceStr) throws Exception { + String publicKey = super.parameters.getString(PUBLIC_KEY); + Assert.isNotEmpty(publicKey, PUBLIC_KEY + " 不能为空"); + byte[] decoded = Base64.getDecoder().decode(publicKey); + RSAPublicKey pubKey = (RSAPublicKey) KeyFactory + .getInstance("RSA") + .generatePublic(new X509EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(sourceStr.getBytes(StandardCharsets.UTF_8))); + } + + @Override + protected String decryptImpl(String cryptoStr) throws Exception { + String privateKey = super.parameters.getString(PRIVATE_KEY); + Assert.isNotEmpty(privateKey, PRIVATE_KEY + " 不能为空"); + byte[] inputByte = Base64.getDecoder().decode(cryptoStr.getBytes(StandardCharsets.UTF_8)); + byte[] decoded = Base64.getDecoder().decode(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, priKey); + return new String(cipher.doFinal(inputByte)); + } + + /** + * 生成 512大小 密钥对 + * @return 密钥对对象 + * @throws NoSuchAlgorithmException 生成异常 + */ + public static RsaKey generateKey() throws NoSuchAlgorithmException { + return generateKey(512); + } + + /** + * 生成密钥对 + * @param keySize 密钥对大小 + * @return 密钥对对象 + * @throws NoSuchAlgorithmException 生成异常 + */ + public static RsaKey generateKey(Integer keySize) throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(keySize); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + return new RsaKey( + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()), + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()) + ); + } + + @AllArgsConstructor + @Getter + public static class RsaKey{ + private final String publicKey; + private final String privateKey; + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java new file mode 100755 index 0000000..41f6ca3 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/Assert.java @@ -0,0 +1,89 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import java.util.function.Supplier; + +/** + * 参数校验工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class Assert { + + private Assert(){}; + + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } + + public static void isTrue(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + } + + public static void state(boolean expression, String message) { + if (!expression) { + throw new IllegalStateException(message); + } + } + + public static void state(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalStateException(nullSafeGet(messageSupplier)); + } + } + + public static T isNotNull(T t, String message) { + if (t == null) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotNull(T t, Supplier messageSupplier) { + if (t == null) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + public static T isNotEmpty(T t, String message) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotEmpty(T t, Supplier messageSupplier) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + private static String nullSafeGet(Supplier messageSupplier) { + return (messageSupplier != null ? messageSupplier.get() : null); + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java new file mode 100755 index 0000000..550afe1 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/CompareClassTypeUtils.java @@ -0,0 +1,101 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +/** + * 比较两个类类型 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class CompareClassTypeUtils { + + private CompareClassTypeUtils(){} + + public static boolean compare(Class class1, Class class2){ + if(class1.isAssignableFrom(class2)){ + return true; + } + if(isBoolean(class1) && isBoolean(class2)){ + return true; + } + if(isChar(class1) && isChar(class2)){ + return true; + } + if(isByte(class1) && isByte(class2)){ + return true; + } + if(isShort(class1) && isShort(class2)){ + return true; + } + if(isInt(class1) && isInt(class2)){ + return true; + } + if(isLong(class1) && isLong(class2)){ + return true; + } + if(isFloat(class1) && isFloat(class2)){ + return true; + } + if(isDouble(class1) && isDouble(class2)){ + return true; + } + if(isVoid(class1) && isVoid(class2)){ + return true; + } + return false; + } + + + public static boolean isBoolean(Class class1){ + return class1.isAssignableFrom(Boolean.class) || class1.isAssignableFrom(boolean.class); + } + + public static boolean isChar(Class class1){ + return class1.isAssignableFrom(Character.class) || class1.isAssignableFrom(char.class); + } + + public static boolean isByte(Class class1){ + return class1.isAssignableFrom(Byte.class) || class1.isAssignableFrom(byte.class); + } + + public static boolean isShort(Class class1){ + return class1.isAssignableFrom(Short.class) || class1.isAssignableFrom(short.class); + } + + public static boolean isInt(Class class1){ + return class1.isAssignableFrom(Integer.class) || class1.isAssignableFrom(int.class); + } + + public static boolean isLong(Class class1){ + return class1.isAssignableFrom(Long.class) || class1.isAssignableFrom(long.class); + } + + public static boolean isFloat(Class class1){ + return class1.isAssignableFrom(Float.class) || class1.isAssignableFrom(float.class); + } + + public static boolean isDouble(Class class1){ + return class1.isAssignableFrom(Double.class) || class1.isAssignableFrom(double.class); + } + + public static boolean isVoid(Class class1){ + return class1.isAssignableFrom(Void.class) || class1.isAssignableFrom(void.class); + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java new file mode 100755 index 0000000..cdc5159 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FieldFilter.java @@ -0,0 +1,40 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import java.lang.reflect.Field; + +/** + * 文过滤接口 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@FunctionalInterface +public interface FieldFilter { + + /** + * 过滤 + * + * @param field 当前字段 + * @return true 允许, false 不允许 + */ + boolean filter(Field field); + + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java new file mode 100755 index 0000000..3dd292f --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/FilesUtils.java @@ -0,0 +1,295 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.common.PackageStructure; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +/** + * 文件工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class FilesUtils { + + /** + * 正斜杠 + */ + public static final String SLASH = "/"; + + /** + * 双正斜杠 + */ + public static final String DOUBLE_SLASH = "//"; + + /** + * 反斜杠 + */ + public static final String BACKSLASH = "\\"; + + + /** + * 获取存在的文件 + * + * @param pathStr 文件路径 + * @return File + */ + public static File getExistFile(String pathStr){ + File file = new File(pathStr); + if(file.exists()){ + return file; + } + return null; + } + + /** + * 是否存在文件 + * @param path 文件路径 + * @return boolean + */ + public static boolean existFile(String path){ + if(ObjectUtils.isEmpty(path)){ + return false; + } + return new File(path).exists(); + } + + + /** + * 拼接file路径 + * + * @param paths 拼接的路径 + * @return 拼接的路径 + * @since 3.0.0 + */ + public static String joiningFilePath(String ...paths) { + if (paths == null || paths.length == 0) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + int length = paths.length; + for (int i = 0; i < length; i++) { + String path = paths[i]; + if (ObjectUtils.isEmpty(path)) { + continue; + } + if (i < length - 1) { + if (path.endsWith(SLASH)) { + path = path.substring(0, path.lastIndexOf(SLASH)); + } else if (path.endsWith(BACKSLASH)) { + path = path.substring(0, path.lastIndexOf(BACKSLASH)); + } else if (path.endsWith(DOUBLE_SLASH)) { + path = path.substring(0, path.lastIndexOf(DOUBLE_SLASH)); + } + } + if (i > 0) { + if (path.startsWith(File.separator) || path.startsWith(SLASH) || + path.startsWith(DOUBLE_SLASH) || path.startsWith(BACKSLASH)) { + stringBuilder.append(path); + } else { + stringBuilder.append(File.separator).append(path); + } + } else { + stringBuilder.append(path); + } + } + + return stringBuilder.toString(); + } + + /** + * 拼接 zip /jar 路径 + * + * @param paths 拼接的路径 + * @return 拼接的路径 + * @since 3.1.0 + */ + public static String joiningZipPath(String ...paths){ + if(paths == null || paths.length == 0){ + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + int length = paths.length; + for (int i = 0; i < length; i++) { + String path = paths[i]; + if(ObjectUtils.isEmpty(path)) { + continue; + } + if(i < length - 1){ + if(path.endsWith(SLASH)){ + path = path.replace(SLASH, ""); + } else if(path.endsWith(BACKSLASH)){ + path = path.replace(BACKSLASH, ""); + } else if(path.endsWith(DOUBLE_SLASH)){ + path = path.replace(DOUBLE_SLASH, ""); + } + } + if(i > 0){ + if(path.startsWith(PackageStructure.SEPARATOR)){ + stringBuilder.append(path); + } else { + stringBuilder.append(PackageStructure.SEPARATOR).append(path); + } + } else { + stringBuilder.append(path); + } + } + + return stringBuilder.toString(); + } + + public static File createFile(String path) throws IOException { + try { + File file = new File(path); + if(file.exists()){ + return file; + } + File parentFile = file.getParentFile(); + if(!parentFile.exists()){ + if(!parentFile.mkdirs()){ + throw new IOException("Create " + parentFile + " dir error"); + } + } + if(file.createNewFile()){ + return file; + } + throw new IOException("Create " + path + " file error"); + } catch (Exception e){ + throw new IOException("Create " + path + " file error"); + } + } + + /** + * 解决相对路径 + * @param rootPath 根路径 + * @param relativePath 以 ~ 开头的相对路径 + * @return 处理后的路径 + */ + public static String resolveRelativePath(String rootPath, String relativePath){ + if(ObjectUtils.isEmpty(relativePath)){ + return relativePath; + } + if(isRelativePath(relativePath)){ + String resolveRelativePath = relativePath.replaceFirst(Constants.RELATIVE_SIGN, ""); + return joiningFilePath(rootPath, resolveRelativePath); + } else { + return relativePath; + } + } + + /** + * 解决存在的相对路径 + * @param rootPath 根路径 + * @param path 以 ~ 开头的相对路径或者完整路径 + * @return File 或者 null(不存在) + */ + public static File resolveExistRelativePathFile(String rootPath, String path){ + if(ObjectUtils.isEmpty(path)){ + return null; + } + if(isRelativePath(path)){ + String resolveRelativePath = path.replaceFirst(Constants.RELATIVE_SIGN, ""); + String joiningFilePath = joiningFilePath(rootPath, resolveRelativePath); + return getExistFile(joiningFilePath); + } else { + File existFile = getExistFile(path); + if(existFile != null){ + return existFile; + } + String joiningFilePath = joiningFilePath(rootPath, path); + return getExistFile(joiningFilePath); + } + } + + /** + * 是否是相对路径 + * @param path 路径 + * @return true 为相对路径, false 未非相对路径 + */ + public static boolean isRelativePath(String path){ + if(ObjectUtils.isEmpty(path)){ + return false; + } + return path.startsWith(Constants.RELATIVE_SIGN); + } + + /** + * 判断两文件是否在同一个目录下 + * @param file1 文件1 + * @param file2 文件2 + * @return 所属目录 + */ + public static File sameParent(File file1, File file2){ + if(file1 == null || file2 == null){ + return null; + } + File parentFile = file1.getParentFile(); + if(parentFile.equals(file2.getParentFile())){ + return parentFile; + } + return null; + } + + /** + * 判断某个目录是否存在于根目录下 + * @param rootPath 根目录 + * @param comparePath 比较的目录 + * @return boolean + */ + public static boolean includePath(Path rootPath, Path comparePath){ + if(rootPath == null || comparePath == null){ + return false; + } + return comparePath.toString().startsWith(rootPath.toString()); + } + + /** + * 是否为子文件 + * @param rootFile rootFile + * @param childFile 子文件 + * @return boolean + */ + public static boolean isChildFile(List rootFile, File childFile){ + if(ObjectUtils.isEmpty(rootFile) || childFile == null || !childFile.exists()){ + return false; + } + for (String fileStr : rootFile) { + File file = new File(fileStr); + if(!file.exists()){ + continue; + } + File[] files = file.listFiles(); + if(files == null){ + continue; + } + for (File f : files) { + if(f.equals(childFile)){ + return true; + } + } + } + return false; + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/MapValueGetter.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/MapValueGetter.java new file mode 100755 index 0000000..862a768 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/MapValueGetter.java @@ -0,0 +1,110 @@ +package com.gitee.starblues.utils; + +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; + +/** + * map 值获取者工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class MapValueGetter { + + private final Map source; + + public MapValueGetter(Map source) { + if(source == null){ + this.source = Collections.emptyMap(); + } else { + this.source = source; + } + } + + public Map getSource(){ + return source; + } + + /** + * 获取object + * @param key map key + * @return value + */ + public Object getObject(String key) { + return source.get(key); + } + + + /** + * 获取 String 类型值 + * @param key map key + * @return String value + */ + public String getString(String key) { + return getValue(key, ObjectValueUtils::getString); + } + + /** + * 获取 Integer 类型值 + * @param key map key + * @return Integer value + */ + public Integer getInteger(String key) { + return getValue(key, ObjectValueUtils::getInteger); + } + + /** + * 获取 Long 类型值 + * @param key map key + * @return Long value + */ + public Long getLong(String key) { + return getValue(key, ObjectValueUtils::getLong); + } + + /** + * 获取 Double 类型值 + * @param key map key + * @return Double value + */ + public Double getDouble(String key) { + return getValue(key, ObjectValueUtils::getDouble); + } + + /** + * 获取 Float 类型值 + * @param key map key + * @return Float value + */ + public Float getFloat(String key) { + return getValue(key, ObjectValueUtils::getFloat); + } + + /** + * 获取 Boolean 类型值 + * @param key map key + * @return Boolean value + */ + public Boolean getBoolean(String key) { + return getValue(key, ObjectValueUtils::getBoolean); + } + + /** + * 获取值并根据自定义实现转换类型 + * @param key map key + * @param function 自定义类型转换 + * @param 转换后的类型泛型 + * @return 转换后的类型值 + */ + private T getValue(String key, Function function){ + Object value = getObject(key); + if(value == null){ + return null; + } + return function.apply(value); + } + + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java new file mode 100755 index 0000000..7e0172c --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectUtils.java @@ -0,0 +1,485 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import java.lang.reflect.Array; +import java.util.*; + +/** + * Object 工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class ObjectUtils { + + private static final String[] EMPTY_STRING_ARRAY = {}; + + private static final String FOLDER_SEPARATOR = "/"; + + private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; + + private static final String TOP_PATH = ".."; + + private static final String CURRENT_PATH = "."; + + private static final char EXTENSION_SEPARATOR = '.'; + + private ObjectUtils(){} + + /** + * 字符串是否为空 + * + * @param charSequence 字符串 + * @return true 为空, false 不为空 + */ + public static boolean isEmpty(CharSequence charSequence) { + return charSequence == null || charSequence.length() == 0; + } + + /** + * 对象是否为空 + * + * @param obj 对象 + * @return true 为空, false 不为空 + */ + public static boolean isEmpty(Object obj) { + if (obj == null) { + return true; + } + + if (obj instanceof Optional) { + return !((Optional) obj).isPresent(); + } + if (obj instanceof CharSequence) { + return ((CharSequence) obj).length() == 0; + } + if (obj.getClass().isArray()) { + return Array.getLength(obj) == 0; + } + if (obj instanceof Collection) { + return ((Collection) obj).isEmpty(); + } + if (obj instanceof Map) { + return ((Map) obj).isEmpty(); + } + return false; + } + + /** + * 是否存在字符 + * + * @param str str + * @return true 存在, false 不存在 + */ + public static boolean hasText(CharSequence str) { + return (str != null && str.length() > 0 && containsText(str)); + } + + /** + * 设置数组字符的空格 + * + * @param array array + * @return result array + */ + public static String[] trimArrayElements(String[] array) { + if (isEmpty(array)) { + return array; + } + + String[] result = new String[array.length]; + for (int i = 0; i < array.length; i++) { + String element = array[i]; + result[i] = (element != null ? element.trim() : null); + } + return result; + } + + + /** + * 将逗号分割的字符串转换为字符转租 + * + * @param str 逗号分割的字符 + * @return 字符数组 + */ + public static String[] commaDelimitedListToStringArray(String str) { + return delimitedListToStringArray(str, ","); + } + + /** + * 自定义符号分割的字符串转换为字符转租 + * + * @param str 待处理字符串 + * @param delimiter 分割符号 + * @return 字符数组 + */ + public static String[] delimitedListToStringArray(String str, String delimiter) { + return delimitedListToStringArray(str, delimiter, null); + } + + /** + * 自定义符号分割的字符串转换为字符转租 + * + * @param str 待处理字符串 + * @param delimiter 分割符号 + * @param charsToDelete 要删除的字符 + * @return 字符数组 + */ + public static String[] delimitedListToStringArray( + String str, String delimiter, String charsToDelete) { + + if (str == null) { + return EMPTY_STRING_ARRAY; + } + if (delimiter == null) { + return new String[] {str}; + } + + List result = new ArrayList<>(); + if (delimiter.isEmpty()) { + for (int i = 0; i < str.length(); i++) { + result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); + } + } + else { + int pos = 0; + int delPos; + while ((delPos = str.indexOf(delimiter, pos)) != -1) { + result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); + pos = delPos + delimiter.length(); + } + if (str.length() > 0 && pos <= str.length()) { + // Add rest of String, but not in case of empty input. + result.add(deleteAny(str.substring(pos), charsToDelete)); + } + } + return toStringArray(result); + } + + /** + * 将集合字符串转换为数组字符串 + * + * @param collection 集合 + * @return 数字字符串 + */ + public static String[] toStringArray(Collection collection) { + return (!isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); + } + + /** + * 从字符串中删除某个字符字符 + * + * @param inString 待处理字符串 + * @param charsToDelete 要删除的字符 + * @return 处理后的字符串 + */ + public static String deleteAny(String inString, String charsToDelete) { + if (!hasLength(inString) || !hasLength(charsToDelete)) { + return inString; + } + + int lastCharIndex = 0; + char[] result = new char[inString.length()]; + for (int i = 0; i < inString.length(); i++) { + char c = inString.charAt(i); + if (charsToDelete.indexOf(c) == -1) { + result[lastCharIndex++] = c; + } + } + if (lastCharIndex == inString.length()) { + return inString; + } + return new String(result, 0, lastCharIndex); + } + + /** + * 字符串是否存在长度 + * + * @param str 字符串 + * @return true 存在长度, false 不存在长度 + */ + public static boolean hasLength(String str) { + return (str != null && !str.isEmpty()); + } + + /** + * 将集合字转换为逗号分割的字符串 + * + * @param coll 集合 + * @return 字符串 + */ + public static String collectionToCommaDelimitedString(Collection coll) { + return collectionToDelimitedString(coll, ","); + } + + /** + * 将集合字转换为逗号分割的字符串 + * + * @param coll 集合 + * @param delim 分割符号 + * @return 字符串 + */ + public static String collectionToDelimitedString(Collection coll, String delim) { + return collectionToDelimitedString(coll, delim, "", ""); + } + + public static String collectionToDelimitedString( + Collection coll, String delim, String prefix, String suffix) { + + if (isEmpty(coll)) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + Iterator it = coll.iterator(); + while (it.hasNext()) { + sb.append(prefix).append(it.next()).append(suffix); + if (it.hasNext()) { + sb.append(delim); + } + } + return sb.toString(); + } + + + public static boolean containsElement(Object[] array, Object element) { + if (array == null) { + return false; + } + for (Object arrayEle : array) { + if (nullSafeEquals(arrayEle, element)) { + return true; + } + } + return false; + } + + public static boolean equalsElement(Object element, Object... objects) { + if (element == null || objects == null) { + return false; + } + for (Object obj : objects) { + if (Objects.equals(element, obj)) { + return true; + } + } + return false; + } + + public static boolean nullSafeEquals(Object o1, Object o2) { + if (o1 == o2) { + return true; + } + if (o1 == null || o2 == null) { + return false; + } + if (o1.equals(o2)) { + return true; + } + if (o1.getClass().isArray() && o2.getClass().isArray()) { + return arrayEquals(o1, o2); + } + return false; + } + + public static boolean endsWithIgnoreCase(String str, String suffix) { + return (str != null && suffix != null && str.length() >= suffix.length() && + str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length())); + } + + public static String getFilenameExtension(String path) { + if (path == null) { + return null; + } + + int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); + if (extIndex == -1) { + return null; + } + + int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); + if (folderIndex > extIndex) { + return null; + } + + return path.substring(extIndex + 1); + } + + public static String cleanPath(String path) { + if (!hasLength(path)) { + return path; + } + String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); + + // Shortcut if there is no work to do + if (pathToUse.indexOf('.') == -1) { + return pathToUse; + } + + int prefixIndex = pathToUse.indexOf(':'); + String prefix = ""; + if (prefixIndex != -1) { + prefix = pathToUse.substring(0, prefixIndex + 1); + if (prefix.contains(FOLDER_SEPARATOR)) { + prefix = ""; + } + else { + pathToUse = pathToUse.substring(prefixIndex + 1); + } + } + if (pathToUse.startsWith(FOLDER_SEPARATOR)) { + prefix = prefix + FOLDER_SEPARATOR; + pathToUse = pathToUse.substring(1); + } + + String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); + Deque pathElements = new ArrayDeque<>(); + int tops = 0; + + for (int i = pathArray.length - 1; i >= 0; i--) { + String element = pathArray[i]; + if (CURRENT_PATH.equals(element)) { + // Points to current directory - drop it. + } + else if (TOP_PATH.equals(element)) { + // Registering top path found. + tops++; + } + else { + if (tops > 0) { + // Merging path element with element corresponding to top path. + tops--; + } + else { + // Normal path element found. + pathElements.addFirst(element); + } + } + } + + // All path elements stayed the same - shortcut + if (pathArray.length == pathElements.size()) { + return prefix + pathToUse; + } + // Remaining top paths need to be retained. + for (int i = 0; i < tops; i++) { + pathElements.addFirst(TOP_PATH); + } + // If nothing else left, at least explicitly point to current path. + if (pathElements.size() == 1 && pathElements.getLast().isEmpty() && !prefix.endsWith(FOLDER_SEPARATOR)) { + pathElements.addFirst(CURRENT_PATH); + } + + return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); + } + + + public static String replace(String inString, String oldPattern, String newPattern) { + if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { + return inString; + } + int index = inString.indexOf(oldPattern); + if (index == -1) { + // no occurrence -> can return input as-is + return inString; + } + + int capacity = inString.length(); + if (newPattern.length() > oldPattern.length()) { + capacity += 16; + } + StringBuilder sb = new StringBuilder(capacity); + + int pos = 0; // our position in the old string + int patLen = oldPattern.length(); + while (index >= 0) { + sb.append(inString, pos, index); + sb.append(newPattern); + pos = index + patLen; + index = inString.indexOf(oldPattern, pos); + } + + // append any characters to the right of a match + sb.append(inString, pos, inString.length()); + return sb.toString(); + } + + + public static int countOccurrencesOf(String str, String sub) { + if (!hasLength(str) || !hasLength(sub)) { + return 0; + } + + int count = 0; + int pos = 0; + int idx; + while ((idx = str.indexOf(sub, pos)) != -1) { + ++count; + pos = idx + sub.length(); + } + return count; + } + + public static String changePackageToMatch(String packageName){ + Assert.isNotEmpty(packageName, "参数 packageName 不能为空"); + return packageName.replace(".", "/") + "/**"; + } + + private static boolean containsText(CharSequence str) { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + private static boolean arrayEquals(Object o1, Object o2) { + if (o1 instanceof Object[] && o2 instanceof Object[]) { + return Arrays.equals((Object[]) o1, (Object[]) o2); + } + if (o1 instanceof boolean[] && o2 instanceof boolean[]) { + return Arrays.equals((boolean[]) o1, (boolean[]) o2); + } + if (o1 instanceof byte[] && o2 instanceof byte[]) { + return Arrays.equals((byte[]) o1, (byte[]) o2); + } + if (o1 instanceof char[] && o2 instanceof char[]) { + return Arrays.equals((char[]) o1, (char[]) o2); + } + if (o1 instanceof double[] && o2 instanceof double[]) { + return Arrays.equals((double[]) o1, (double[]) o2); + } + if (o1 instanceof float[] && o2 instanceof float[]) { + return Arrays.equals((float[]) o1, (float[]) o2); + } + if (o1 instanceof int[] && o2 instanceof int[]) { + return Arrays.equals((int[]) o1, (int[]) o2); + } + if (o1 instanceof long[] && o2 instanceof long[]) { + return Arrays.equals((long[]) o1, (long[]) o2); + } + if (o1 instanceof short[] && o2 instanceof short[]) { + return Arrays.equals((short[]) o1, (short[]) o2); + } + return false; + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectValueUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectValueUtils.java new file mode 100755 index 0000000..ad9274d --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ObjectValueUtils.java @@ -0,0 +1,91 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +/** + * object value convert utils + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public abstract class ObjectValueUtils { + + private ObjectValueUtils(){ + } + + public static String getString(Object value){ + if(value == null){ + return null; + } + if(value instanceof CharSequence){ + return ((CharSequence) value).toString(); + } + return String.valueOf(value); + } + + public static Integer getInteger(Object value){ + if(value == null){ + return null; + } + if(value instanceof Integer){ + return (Integer) value; + } + return Integer.parseInt(String.valueOf(value)); + } + + public static Long getLong(Object value){ + if(value == null){ + return null; + } + if(value instanceof Long){ + return (Long) value; + } + return Long.parseLong(String.valueOf(value)); + } + + public static Double getDouble(Object value) { + if(value == null){ + return null; + } + if(value instanceof Double){ + return (Double) value; + } + return Double.parseDouble(String.valueOf(value)); + } + + public static Float getFloat(Object value) { + if(value == null){ + return null; + } + if(value instanceof Float){ + return (Float) value; + } + return Float.parseFloat(String.valueOf(value)); + } + + public static Boolean getBoolean(Object value) { + if(value == null){ + return null; + } + if(value instanceof Boolean){ + return (Boolean) value; + } + return Boolean.parseBoolean(String.valueOf(value)); + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java new file mode 100755 index 0000000..d2c2d86 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/PropertiesUtils.java @@ -0,0 +1,69 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import java.util.Properties; + +/** + * 操作 Manifest 工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class PropertiesUtils { + + private PropertiesUtils(){} + + /** + * 获取值 + * + * @param properties properties + * @param key 获取的key + * @return 获取的值或者null + */ + public static String getValue(Properties properties, String key){ + return getValue(properties, key, true); + } + + /** + * 获取值 + * + * @param properties properties + * @param key 获取的key + * @param notExitsThrowException 如果不存在是否抛出异常 + * @return 获取的值 + */ + public static String getValue(Properties properties, String key, boolean notExitsThrowException){ + boolean throwException = false; + String value = null; + try { + value = properties.getProperty(key); + if(value == null && notExitsThrowException){ + throwException = true; + } + } catch (Throwable e){ + // 忽略 + throwException = true; + } + if(throwException){ + throw new IllegalStateException("Not found '" + key + "' config!"); + } + return value; + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java new file mode 100755 index 0000000..4e7ec3d --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ReflectionUtils.java @@ -0,0 +1,248 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * 反射工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class ReflectionUtils { + + private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + + private ReflectionUtils(){} + + public static Field findField(Class clazz, String name) { + return findField(clazz, name, null); + } + + public static Field findField(Class clazz, Class fieldType) { + return findField(clazz, null, fieldType); + } + + public static Field findField(Class clazz, String fieldName, Class fieldType) { + if(fieldName == null && fieldType == null){ + throw new IllegalArgumentException("fieldName or fieldType of the field must be specified"); + } + return findField(clazz, (field)->{ + if ((fieldName == null || fieldName.equals(field.getName())) && + (fieldType == null || fieldType.equals(field.getType()))) { + return true; + } + return false; + }); + } + + public static Field findField(Class clazz, FieldFilter filter) { + Objects.requireNonNull(clazz, "clazz must not be null"); + + + Field[] declaredFields = clazz.getDeclaredFields(); + while (true){ + for (Field field : declaredFields) { + if (filter.filter(field)) { + return field; + } + } + clazz = clazz.getSuperclass(); + if(clazz == null || clazz == Object.class){ + break; + } + declaredFields = clazz.getDeclaredFields(); + } + + return null; + } + + + public static Object getField(Object object, Class objectClass, String fieldName) { + return getField(object, objectClass, fieldName, null); + } + + public static Object getField(Object object, Class objectClass, String fieldName, Class fieldType) { + Field field = findField(objectClass, fieldName, fieldType); + if(field == null){ + return null; + } + field.setAccessible(true); + try { + return field.get(object); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + + public static void setField(Object o, String fieldName, Object value) { + setField(o, fieldName, null, value); + } + + public static void setField(Object o, String fieldName, Class fieldType, Object value) { + Field field = findField(o.getClass(), fieldName, fieldType); + if(field != null){ + field.setAccessible(true); + try { + field.set(o, value); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + + public static Method findMethod(Class clazz, String name) { + return findMethod(clazz, name, EMPTY_CLASS_ARRAY); + } + + public static Method findMethod(Class clazz, String name, Class... paramTypes) { + List methods = findMethods(clazz, name, paramTypes); + if(ObjectUtils.isEmpty(methods)){ + return null; + } else { + return methods.get(0); + } + } + + public static List findMethods(Class clazz, String name, Class... paramTypes){ + Assert.isNotNull(clazz, "Class must not be null"); + Assert.isNotEmpty(name, "Method name must not be null"); + Class searchType = clazz; + List methodList = new ArrayList<>(); + while (searchType != null) { + Method[] methods = (searchType.isInterface() ? searchType.getMethods() : + getDeclaredMethods(searchType, false)); + for (Method method : methods) { + if (name.equals(method.getName()) && (paramTypes == null || hasSameParams(method, paramTypes))) { + methodList.add(method); + } + } + searchType = searchType.getSuperclass(); + } + return methodList; + } + + @SuppressWarnings("unchecked") + public static T invoke(Object object, String methodName, Object... params) throws RuntimeException { + Class[] paramTypes = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + paramTypes[i] = params[i].getClass(); + } + Class aClass = object.getClass(); + Method method = ReflectionUtils.findMethod(aClass, methodName, paramTypes); + if(method == null){ + throw new RuntimeException("Not found method : " + methodToString(aClass, methodName, paramTypes)); + } + try { + Object invoke = method.invoke(object, params); + if(invoke != null){ + return (T) invoke; + } else { + return null; + } + } catch (Exception e) { + throw new RuntimeException("Cannot call method : " + methodToString(aClass, methodName, paramTypes), e); + } + } + + public static void setAttribute(Object bean, String setMethodName, Object setObject) throws Exception { + Class aClass = bean.getClass(); + Method setMethod = ReflectionUtils.findMethod(aClass, setMethodName, setObject.getClass()); + + if(setMethod == null){ + throw new Exception("Not found method[" + setMethodName + "] of :" + aClass.getName()); + } + setMethod.invoke(bean, setObject); + } + + private static Method[] getDeclaredMethods(Class clazz, boolean defensive) { + Assert.isNotNull(clazz, "Class must not be null"); + Method[] result = new Method[]{}; + try { + Method[] declaredMethods = clazz.getDeclaredMethods(); + List defaultMethods = findConcreteMethodsOnInterfaces(clazz); + + if (defaultMethods != null) { + result = new Method[declaredMethods.length + defaultMethods.size()]; + System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); + int index = declaredMethods.length; + for (Method defaultMethod : defaultMethods) { + result[index] = defaultMethod; + index++; + } + } else { + result = declaredMethods; + } + } catch (Throwable ex) { + throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() + + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); + } + return result; + } + + private static List findConcreteMethodsOnInterfaces(Class clazz) { + List result = null; + for (Class ifc : clazz.getInterfaces()) { + for (Method ifcMethod : ifc.getMethods()) { + if (!Modifier.isAbstract(ifcMethod.getModifiers())) { + if (result == null) { + result = new ArrayList<>(); + } + result.add(ifcMethod); + } + } + } + return result; + } + + private static boolean hasSameParams(Method method, Class[] paramTypes) { + if(paramTypes.length != method.getParameterCount()){ + return false; + } + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < paramTypes.length; i++) { + Class paramType = paramTypes[i]; + Class methodParamType = parameterTypes[i]; + if(!CompareClassTypeUtils.compare(methodParamType, paramType)){ + return false; + } + } + return true; + } + + + public static NoSuchMethodException getNoSuchMethodException(Class aClass, String name, Class[] argTypes) { + return new NoSuchMethodException("Not found method:" + methodToString(aClass, name, argTypes)); + } + + public static String methodToString(Class aClass, String name, Class[] argTypes) { + StringJoiner sj = new StringJoiner(", ", aClass.getName() + "." + name + "(", ")"); + if (argTypes != null) { + for (Class c : argTypes) { + sj.add((c == null) ? "null" : c.getName()); + } + } + return sj.toString(); + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java new file mode 100755 index 0000000..1571766 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/ResourceUtils.java @@ -0,0 +1,254 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * 资源工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class ResourceUtils { + + public static final String URL_PROTOCOL_FILE = "file"; + + public final static String TYPE_FILE = "file"; + public final static String TYPE_CLASSPATH = "classpath"; + public final static String TYPE_PACKAGE = "package"; + + public static final String ROOT_PLUGIN_SIGN = "~"; + + public final static String TYPE_SPLIT = ":"; + + public static final String CLASSPATH_URL_PREFIX = "classpath:"; + public static final String FILE_URL_PREFIX = "file:"; + + public static final String PACKAGE_SPLIT = "/"; + public static final String PACKAGE_SPLIT_DOT = "."; + + public static final String JAR_FILE_EXTENSION = ".jar"; + public static final String ZIP_FILE_EXTENSION = ".zip"; + public static final String CLASS_FILE_EXTENSION = ".class"; + public static final String URL_PROTOCOL_VFSFILE = "vfsfile"; + public static final String URL_PROTOCOL_VFS = "vfs"; + + /** + * 获取匹配路绝 + * @param locationMatch 原始匹配路径。规则为: file:xxx, classpath:xxx , package:xxx + * @return 整合出完整的匹配路绝 + */ + public static String getMatchLocation(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return null; + } + String classPathType = TYPE_CLASSPATH + TYPE_SPLIT; + if(isClasspath(locationMatch)){ + return locationMatch.replaceFirst(classPathType, ""); + } + String fileType = TYPE_FILE + TYPE_SPLIT; + if(isFile(locationMatch)){ + return locationMatch.replaceFirst(fileType, ""); + } + String packageType = TYPE_PACKAGE + TYPE_SPLIT; + if(isPackage(locationMatch)){ + String location = locationMatch.replaceFirst(packageType, ""); + return replacePackage(location); + } + return null; + } + + public static boolean isClasspath(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return false; + } + return locationMatch.startsWith(TYPE_CLASSPATH + TYPE_SPLIT); + } + + public static boolean isFile(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return false; + } + return locationMatch.startsWith(TYPE_FILE + TYPE_SPLIT); + } + + public static boolean isPackage(String locationMatch){ + if(ObjectUtils.isEmpty(locationMatch)){ + return false; + } + return locationMatch.startsWith(TYPE_PACKAGE + TYPE_SPLIT); + } + + public static boolean isClass(String path){ + if(ObjectUtils.isEmpty(path)){ + return false; + } + return path.toLowerCase().endsWith(CLASS_FILE_EXTENSION); + } + + /** + * 是否为 zip 文件 + * @param path 文件路径 + * @return boolean + */ + public static boolean isZipFile(Path path) { + return Files.isRegularFile(path) && isZip(path.toString()); + } + + /** + * 是否为 jar 文件 + * @param path 文件路径 + * @return boolean + */ + public static boolean isJarFile(Path path) { + return Files.isRegularFile(path) && isJar(path.toString()); + } + + /** + * 是否为 zip 文件 + * @param name 文件名称 + * @return boolean + */ + public static boolean isZip(String name) { + return name != null && name.toLowerCase().endsWith(ZIP_FILE_EXTENSION); + } + + /** + * 是否为 jar 文件 + * @param name 文件名称 + * @return boolean + */ + public static boolean isJar(String name) { + return name != null && name.toLowerCase().endsWith(JAR_FILE_EXTENSION); + } + + public static boolean isDirFile(Path path) { + return path.toFile().isDirectory(); + } + + public static boolean isJarFileURL(URL url) { + return (URL_PROTOCOL_FILE.equals(url.getProtocol()) || + url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION)); + } + + public static boolean isFileURL(URL url) { + String protocol = url.getProtocol(); + return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) || + URL_PROTOCOL_VFS.equals(protocol)); + } + + + public static boolean existFile(File rootFile, String checkFileName){ + if(rootFile == null || !rootFile.exists()){ + return false; + } + final File[] listFiles = rootFile.listFiles(); + if(listFiles == null){ + return false; + } + for (File listFile : listFiles) { + if(Objects.equals(listFile.getName(), checkFileName)){ + return true; + } + if(listFile.isDirectory()){ + return existFile(listFile, checkFileName); + } + } + return false; + } + + public static void listFile(File rootFile, Consumer consumerFile){ + if(rootFile == null || !rootFile.exists()){ + return; + } + final File[] listFiles = rootFile.listFiles(); + if(listFiles == null || listFiles.length == 0){ + return; + } + for (File listFile : listFiles) { + if(listFile.isDirectory()){ + listFile(listFile, consumerFile); + continue; + } + consumerFile.accept(listFile); + } + } + + public static String replacePackage(String packageName){ + if(packageName == null){ + return null; + } + return packageName.replace(PACKAGE_SPLIT_DOT, PACKAGE_SPLIT); + } + + + public static boolean isUrl(String resourceLocation) { + if (resourceLocation == null) { + return false; + } + if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { + return true; + } + try { + new URL(resourceLocation); + return true; + } + catch (MalformedURLException ex) { + return false; + } + } + + public static void closeQuietly(Object closeable){ + closeQuietly(closeable, null); + } + + public static void closeQuietly(Object closeable, Consumer consumer){ + if (closeable != null) { + try { + if(closeable instanceof AutoCloseable){ + ((AutoCloseable) closeable).close(); + } + } catch (final Exception e) { + if (consumer != null) { + consumer.accept(e); + } + } + } + } + + public static String getFileSuffix(String fileName){ + if(fileName == null){ + return null; + } + int i = fileName.lastIndexOf("."); + if(i > 0 && fileName.length() >= (i + 1)){ + return fileName.substring(i + 1); + } else { + return ""; + } + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java new file mode 100755 index 0000000..6961d37 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/StringUtils.java @@ -0,0 +1,79 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringTokenizer; + +/** + * String 工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class StringUtils { + + private static final String[] EMPTY_STRING_ARRAY = {}; + + private static String[] tokenizeLocaleSource(String localeSource) { + return tokenizeToStringArray(localeSource, "_ ", false, false); + } + + public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { + + if (str == null) { + return EMPTY_STRING_ARRAY; + } + + StringTokenizer st = new StringTokenizer(str, delimiters); + List tokens = new ArrayList<>(); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + if (!ignoreEmptyTokens || token.length() > 0) { + tokens.add(token); + } + } + return toStringArray(tokens); + } + + public static String[] toStringArray(Collection collection) { + return (!ObjectUtils.isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); + } + + public static String toStrByArray(String[] str){ + if(str == null || str.length == 0){ + return ""; + } + int length = str.length; + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < length; i++) { + String s = str[i]; + stringBuilder.append(s); + if (i < length - 1) { + stringBuilder.append(","); + } + } + return stringBuilder.toString(); + } + +} diff --git a/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java new file mode 100755 index 0000000..568a198 --- /dev/null +++ b/iot-spring-brick/spring-brick-common/src/main/java/com/gitee/starblues/utils/UrlUtils.java @@ -0,0 +1,139 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +/** + * http url util + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class UrlUtils { + + private UrlUtils(){} + + public static final String PATH_SEPARATOR = "/"; + + public final static String SEPARATOR_DOT = "."; + public final static String SEPARATOR_BACKSLASH = "\\"; + + /** + * rest接口拼接路径 + * + * @param path1 路径1 + * @param path2 路径2 + * @return 拼接的路径 + * @since 3.0.0 + */ + public static String restJoiningPath(String path1, String path2){ + if(path1 != null && path2 != null){ + if(path1.endsWith(PATH_SEPARATOR) && path2.startsWith(PATH_SEPARATOR)){ + return path1 + path2.substring(1); + } else if(!path1.endsWith(PATH_SEPARATOR) && !path2.startsWith(PATH_SEPARATOR)){ + return path1 + PATH_SEPARATOR + path2; + } else { + return path1 + path2; + } + } else if(path1 != null){ + return path1; + } else if(path2 != null){ + return path2; + } else { + return ""; + } + } + + + /** + * 拼接url路径 + * + * @param paths 拼接的路径 + * @return 拼接的路径 + * @since 3.0.0 + */ + public static String joiningUrlPath(String ...paths){ + if(paths == null || paths.length == 0){ + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + int length = paths.length; + for (int i = 0; i < length; i++) { + String path = paths[i]; + if(ObjectUtils.isEmpty(path)) { + continue; + } + if((i < length - 1) && path.endsWith(PATH_SEPARATOR)){ + path = path.substring(path.lastIndexOf(PATH_SEPARATOR)); + } + if(path.startsWith(PATH_SEPARATOR)){ + stringBuilder.append(path); + } else { + stringBuilder.append(PATH_SEPARATOR).append(path); + } + } + + return stringBuilder.toString(); + } + + /** + * 格式化 url + * @param url 原始url + * @return 格式化后的url + * @since 3.0.0 + */ + public static String format(String url){ + if(ObjectUtils.isEmpty(url)){ + return url; + } + String[] split = url.split(PATH_SEPARATOR); + StringBuilder stringBuilder = new StringBuilder(); + int length = split.length; + for (int i = 0; i < length; i++) { + String str = split[i]; + if(ObjectUtils.isEmpty(str)){ + continue; + } + if(i < length - 1){ + stringBuilder.append(str).append(PATH_SEPARATOR); + } else { + stringBuilder.append(str); + } + } + return stringBuilder.toString(); + } + + /** + * 格式化匹配url时的格式 + * @param url url + * @return 格式化后 + * @since 3.0.3 + */ + public static String formatMatchUrl(String url){ + if(url.contains(SEPARATOR_DOT)){ + url = url.replace(SEPARATOR_DOT, PATH_SEPARATOR); + } + if(url.contains(SEPARATOR_BACKSLASH)){ + url = url.replace(SEPARATOR_BACKSLASH, PATH_SEPARATOR); + } + if(url.startsWith(PATH_SEPARATOR)){ + url = url.substring(url.indexOf(PATH_SEPARATOR) + 1); + } + return url; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/pom.xml b/iot-spring-brick/spring-brick-loader/pom.xml new file mode 100755 index 0000000..388379c --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/pom.xml @@ -0,0 +1,16 @@ + + + + com.gitee.starblues + iot-spring-brick + 3.1.4 + + 4.0.0 + + spring-brick-loader + 加载插件模块 + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/DevelopmentMode.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/DevelopmentMode.java new file mode 100755 index 0000000..347c082 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/DevelopmentMode.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader; + +/** + * 开发模式key定义 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public abstract class DevelopmentMode { + + /** + * 隔离模式 + */ + public static final String ISOLATION = "isolation"; + + /** + * 共存模式 + */ + public static final String COEXIST = "coexist"; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/LoaderConstant.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/LoaderConstant.java new file mode 100755 index 0000000..bb7853e --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/LoaderConstant.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader; + +/** + * 常量统一定义 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public class LoaderConstant { + + public static final String PROD_CLASSES_PATH = "classes/"; + public static final String PROD_CLASSES_URL_SIGN = "/classes!/"; + public static final String PROD_LIB_PATH = "lib/"; + + /** + * 相对路径符号标志 + */ + public final static String RELATIVE_SIGN = "~"; + + + /** + * ================= Manifest Key ===================== + */ + public static final String MAIN_LIB_DIR = "Lib-Dir"; + public static final String MAIN_LIB_INDEXES_SPLIT = " "; + + public static final String START_CLASS = "Start-Class"; + public static final String MAIN_DEVELOPMENT_MODE = "Development-Mode"; + public static final String MAIN_PACKAGE_TYPE = "Main-Package-Type"; + + public static final String MAIN_PACKAGE_TYPE_JAR = "jar"; + public static final String MAIN_PACKAGE_TYPE_JAR_OUTER = "jar-outer"; + + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java new file mode 100755 index 0000000..e4ac2cf --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/PluginResourceStorage.java @@ -0,0 +1,184 @@ +/** + * Copyright [2019-Present] [starBlues] + *

+ * Licensed 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 com.gitee.starblues.loader; + +import com.gitee.starblues.loader.jar.AbstractJarFile; +import com.gitee.starblues.loader.jar.JarFile; +import com.gitee.starblues.loader.jar.JarFileWrapper; +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 插件资源存储者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class PluginResourceStorage { + + public final static Map STORAGE_MAP = new ConcurrentHashMap<>(); + + /** + * 添加插件资源 + * @param pluginId 插件id + * @param pluginFileName 插件文件名称 + */ + public static void addPlugin(String pluginId, String pluginFileName, List libPath) { + if (STORAGE_MAP.containsKey(pluginId)) { + return; + } + STORAGE_MAP.put(pluginId, new Storage(pluginFileName, libPath)); + } + + /** + * 移除插件 + * @param pluginId 插件 + */ + public static void removePlugin(String pluginId) { + Storage storage = STORAGE_MAP.get(pluginId); + if (storage == null) { + return; + } + IOUtils.closeQuietly(storage); + STORAGE_MAP.remove(pluginId); + } + + /** + * 添加插件jar文件 + * @param jarFile jar插件文件 + */ + public static void addJarFile(AbstractJarFile jarFile) { + STORAGE_MAP.forEach((k, v) -> { + v.addJarFile(jarFile.getName(), jarFile); + }); + } + + /** + * 添加插件根的jar文件 + * @param file 插件文件 + * @param jarFile 插件jar文件 + */ + public static void addRootJarFile(File file, JarFileWrapper jarFile) { + STORAGE_MAP.forEach((k, v) -> { + v.addRootJarFile(file, jarFile); + }); + } + + /** + * 通过插件文件获取插件jar文件 + * @param file 插件文件 + * @return 插件jar文件 + */ + public static JarFileWrapper getRootJarFile(File file) { + for (Storage value : STORAGE_MAP.values()) { + JarFileWrapper jarFile = value.getRootJarFile(file); + if (jarFile != null) { + return jarFile; + } + } + return null; + } + + + private static class Storage implements Closeable { + private final String pluginFileName; + private final List libPath; + private final Map rootJarFileMap = new ConcurrentHashMap<>(); + private final Map> jarFileMap = new ConcurrentHashMap<>(); + + public Storage(String pluginFileName, List libPath) { + this.pluginFileName = pluginFileName; + if (libPath == null) { + this.libPath = Collections.emptyList(); + } else { + this.libPath = libPath; + } + } + + public void addJarFile(String name, AbstractJarFile jarFile) { + if (name == null || jarFile == null) { + return; + } + if (isAddFile(name)) { + List jarFiles = jarFileMap.computeIfAbsent(name, k -> new ArrayList<>()); + long count = jarFiles.stream().filter(j -> j.getName().equals(jarFile.getName())).count(); + if (count == 0) { + jarFiles.add(jarFile); + } + } + } + + public void addRootJarFile(File file, JarFileWrapper jarFile) { + String absolutePath = file.getAbsolutePath(); + if (isAddFile(absolutePath)) { + rootJarFileMap.put(file, jarFile); + } + } + + public JarFileWrapper getRootJarFile(File file) { + return rootJarFileMap.get(file); + } + + @Override + public void close() throws IOException { + for (List value : jarFileMap.values()) { + for (AbstractJarFile jarFile : value) { + if (jarFile == null) { + continue; + } + if (jarFile instanceof JarFileWrapper) { + ((JarFileWrapper) jarFile).canClosed(); + } + IOUtils.closeQuietly(jarFile); + } + } + for (JarFileWrapper jarFile : rootJarFileMap.values()) { + if (jarFile == null) { + continue; + } + jarFile.canClosed(); + IOUtils.closeQuietly(jarFile); + } + jarFileMap.clear(); + rootJarFileMap.clear(); + } + + private boolean isAddFile(String path) { + if (path.contains(pluginFileName)) { + return true; + } + for (String libPath : libPath) { + if (path.contains(libPath)) { + return true; + } + } + return false; + } + + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java new file mode 100755 index 0000000..11f95d6 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/Archive.java @@ -0,0 +1,145 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.archive; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.*; +import java.util.function.Consumer; +import java.util.jar.Manifest; + +/** + * An archive that can be launched + * + * @author Phillip Webb + * @since 1.0.0 + * @see JarFileArchive + */ +public interface Archive extends Iterable, AutoCloseable { + + /** + * Returns a URL that can be used to load the archive. + * @return the archive URL + * @throws MalformedURLException if the URL is malformed + */ + URL getUrl() throws MalformedURLException; + + /** + * Returns the manifest of the archive. + * @return the manifest + * @throws IOException if the manifest cannot be read + */ + Manifest getManifest() throws IOException; + + /** + * Returns nested {@link Archive}s for entries that match the specified filters. + * @param searchFilter filter used to limit when additional sub-entry searching is + * required or {@code null} if all entries should be considered. + * @param includeFilter filter used to determine which entries should be included in + * the result or {@code null} if all entries should be included + * @return the nested archives + * @throws IOException io exception + * @since 2.3.0 + */ + Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException; + + /** + * Return a new iterator for the archive entries. + * @return {@link Iterable} + */ + @Override + Iterator iterator(); + + /** + * Performs the given action for each element of the {@code Iterable} until all + * elements have been processed or the action throws an exception. + * @param action consumer entry + */ + @Override + default void forEach(Consumer action) { + Objects.requireNonNull(action); + for (Entry entry : this) { + action.accept(entry); + } + } + + /** + * Creates a {@link Spliterator} over the elements described by this {@code Iterable}. + * @see Iterable#spliterator + * @return {@link Spliterator} + */ + @Override + default Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), 0); + } + + /** + * Return if the archive is exploded (already unpacked). + * @return if the archive is exploded + * @since 2.3.0 + */ + default boolean isExploded() { + return false; + } + + /** + * Closes the {@code Archive}, releasing any open resources. + * @throws Exception if an error occurs during close processing + * @since 2.2.0 + */ + @Override + default void close() throws Exception { + + } + + /** + * Represents a single entry in the archive. + */ + interface Entry { + + /** + * Returns {@code true} if the entry represents a directory. + * @return if the entry is a directory + */ + boolean isDirectory(); + + /** + * Returns the name of the entry. + * @return the name of the entry + */ + String getName(); + + } + + /** + * Strategy interface to filter {@link Entry Entries}. + */ + @FunctionalInterface + interface EntryFilter { + + /** + * Apply the jar entry filter. + * @param entry the entry to filter + * @return {@code true} if the filter matches + */ + boolean matches(Entry entry); + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java new file mode 100755 index 0000000..7ec7f03 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/ExplodedArchive.java @@ -0,0 +1,338 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.archive; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.*; +import java.util.jar.Manifest; + +/** + * {@link Archive} implementation backed by an exploded archive directory. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Madhura Bhave + * @since 1.0.0 + */ +public class ExplodedArchive implements Archive { + + private static final Set SKIPPED_NAMES = new HashSet<>(Arrays.asList(".", "..")); + + private final File root; + + private final boolean recursive; + + private final File manifestFile; + + private Manifest manifest; + + /** + * Create a new {@link ExplodedArchive} instance. + * @param root the root directory + */ + public ExplodedArchive(File root) { + this(root, true); + } + + /** + * Create a new {@link ExplodedArchive} instance. + * @param root the root directory + * @param recursive if recursive searching should be used to locate the manifest. + * Defaults to {@code true}, directories with a large tree might want to set this to + * {@code false}. + */ + public ExplodedArchive(File root, boolean recursive) { + if (!root.exists() || !root.isDirectory()) { + throw new IllegalArgumentException("Invalid source directory " + root); + } + this.root = root; + this.recursive = recursive; + this.manifestFile = getManifestFile(root); + } + + private File getManifestFile(File root) { + File metaInf = new File(root, "META-INF"); + return new File(metaInf, "MANIFEST.MF"); + } + + @Override + public URL getUrl() throws MalformedURLException { + return this.root.toURI().toURL(); + } + + @Override + public Manifest getManifest() throws IOException { + if (this.manifest == null && this.manifestFile.exists()) { + try (FileInputStream inputStream = new FileInputStream(this.manifestFile)) { + this.manifest = new Manifest(inputStream); + } + } + return this.manifest; + } + + @Override + public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException { + return new ArchiveIterator(this.root, this.recursive, searchFilter, includeFilter); + } + + @Override + public Iterator iterator() { + return new EntryIterator(this.root, this.recursive, null, null); + } + + protected Archive getNestedArchive(Entry entry) throws IOException { + File file = ((FileEntry) entry).getFile(); + return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive((FileEntry) entry)); + } + + @Override + public boolean isExploded() { + return true; + } + + @Override + public String toString() { + try { + return getUrl().toString(); + } + catch (Exception ex) { + return "exploded archive"; + } + } + + /** + * File based {@link Entry} {@link Iterator}. + */ + private abstract static class AbstractIterator implements Iterator { + + private static final Comparator ENTRY_COMPARATOR = Comparator.comparing(File::getAbsolutePath); + + private final File root; + + private final boolean recursive; + + private final EntryFilter searchFilter; + + private final EntryFilter includeFilter; + + private final Deque> stack = new LinkedList<>(); + + private FileEntry current; + + private final String rootUrl; + + AbstractIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) { + this.root = root; + this.rootUrl = this.root.toURI().getPath(); + this.recursive = recursive; + this.searchFilter = searchFilter; + this.includeFilter = includeFilter; + this.stack.add(listFiles(root)); + this.current = poll(); + } + + @Override + public boolean hasNext() { + return this.current != null; + } + + @Override + public T next() { + FileEntry entry = this.current; + if (entry == null) { + throw new NoSuchElementException(); + } + this.current = poll(); + return adapt(entry); + } + + private FileEntry poll() { + while (!this.stack.isEmpty()) { + Iterator peek = this.stack.peek(); + if(peek == null){ + continue; + } + while (peek.hasNext()) { + File file = peek.next(); + if (SKIPPED_NAMES.contains(file.getName())) { + continue; + } + FileEntry entry = getFileEntry(file); + if (isListable(entry)) { + this.stack.addFirst(listFiles(file)); + } + if (this.includeFilter == null || this.includeFilter.matches(entry)) { + return entry; + } + } + this.stack.poll(); + } + return null; + } + + private FileEntry getFileEntry(File file) { + URI uri = file.toURI(); + String name = uri.getPath().substring(this.rootUrl.length()); + try { + return new FileEntry(name, file, uri.toURL()); + } + catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + + private boolean isListable(FileEntry entry) { + return entry.isDirectory() && (this.recursive || entry.getFile().getParentFile().equals(this.root)) + && (this.searchFilter == null || this.searchFilter.matches(entry)) + && (this.includeFilter == null || !this.includeFilter.matches(entry)); + } + + private Iterator listFiles(File file) { + File[] files = file.listFiles(); + if (files == null) { + return Collections.emptyIterator(); + } + Arrays.sort(files, ENTRY_COMPARATOR); + return Arrays.asList(files).iterator(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove"); + } + + protected abstract T adapt(FileEntry entry); + + } + + private static class EntryIterator extends AbstractIterator { + + EntryIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) { + super(root, recursive, searchFilter, includeFilter); + } + + @Override + protected Entry adapt(FileEntry entry) { + return entry; + } + + } + + private static class ArchiveIterator extends AbstractIterator { + + ArchiveIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) { + super(root, recursive, searchFilter, includeFilter); + } + + @Override + protected Archive adapt(FileEntry entry) { + File file = entry.getFile(); + return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive(entry)); + } + + } + + /** + * {@link Entry} backed by a File. + */ + private static class FileEntry implements Entry { + + private final String name; + + private final File file; + + private final URL url; + + FileEntry(String name, File file, URL url) { + this.name = name; + this.file = file; + this.url = url; + } + + File getFile() { + return this.file; + } + + @Override + public boolean isDirectory() { + return this.file.isDirectory(); + } + + @Override + public String getName() { + return this.name; + } + + URL getUrl() { + return this.url; + } + + } + + /** + * {@link Archive} implementation backed by a simple JAR file that doesn't itself + * contain nested archives. + */ + private static class SimpleJarFileArchive implements Archive { + + private final URL url; + + SimpleJarFileArchive(FileEntry file) { + this.url = file.getUrl(); + } + + @Override + public URL getUrl() throws MalformedURLException { + return this.url; + } + + @Override + public Manifest getManifest() throws IOException { + return null; + } + + @Override + public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) + throws IOException { + return Collections.emptyIterator(); + } + + @Override + @Deprecated + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public String toString() { + try { + return getUrl().toString(); + } + catch (Exception ex) { + return "jar archive"; + } + } + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java new file mode 100755 index 0000000..1183476 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/archive/JarFileArchive.java @@ -0,0 +1,303 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.archive; + +import com.gitee.starblues.loader.jar.JarFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.*; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; + +/** + * {@link Archive} implementation backed by a {@link JarFile}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @since 1.0.0 + */ +public class JarFileArchive implements Archive { + + private static final String UNPACK_MARKER = "UNPACK:"; + + private static final int BUFFER_SIZE = 32 * 1024; + + private static final FileAttribute[] NO_FILE_ATTRIBUTES = {}; + + private static final EnumSet DIRECTORY_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE); + + private static final EnumSet FILE_PERMISSIONS = EnumSet.of(PosixFilePermission.OWNER_READ, + PosixFilePermission.OWNER_WRITE); + + private final JarFile jarFile; + + private URL url; + + private Path tempUnpackDirectory; + + public JarFileArchive(File file) throws IOException { + this(file, file.toURI().toURL()); + } + + public JarFileArchive(File file, URL url) throws IOException { + this(new JarFile(file)); + this.url = url; + } + + public JarFileArchive(JarFile jarFile) { + this.jarFile = jarFile; + } + + @Override + public URL getUrl() throws MalformedURLException { + if (this.url != null) { + return this.url; + } + return this.jarFile.getUrl(); + } + + @Override + public Manifest getManifest() throws IOException { + return this.jarFile.getManifest(); + } + + @Override + public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException { + return new NestedArchiveIterator(this.jarFile.iterator(), searchFilter, includeFilter); + } + + @Override + public Iterator iterator() { + return new EntryIterator(this.jarFile.iterator(), null, null); + } + + @Override + public void close() throws IOException { + this.jarFile.close(); + } + + protected Archive getNestedArchive(Entry entry) throws IOException { + JarEntry jarEntry = ((JarFileEntry) entry).getJarEntry(); + if (jarEntry.getComment().startsWith(UNPACK_MARKER)) { + return getUnpackedNestedArchive(jarEntry); + } + try { + JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry); + return new JarFileArchive(jarFile); + } catch (Exception ex) { + throw new IllegalStateException("Failed to get nested archive for entry " + entry.getName(), ex); + } + } + + private Archive getUnpackedNestedArchive(JarEntry jarEntry) throws IOException { + String name = jarEntry.getName(); + if (name.lastIndexOf('/') != -1) { + name = name.substring(name.lastIndexOf('/') + 1); + } + Path path = getTempUnpackDirectory().resolve(name); + if (!Files.exists(path) || Files.size(path) != jarEntry.getSize()) { + unpack(jarEntry, path); + } + return new JarFileArchive(path.toFile(), path.toUri().toURL()); + } + + private Path getTempUnpackDirectory() { + if (this.tempUnpackDirectory == null) { + Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir")); + this.tempUnpackDirectory = createUnpackDirectory(tempDirectory); + } + return this.tempUnpackDirectory; + } + + private Path createUnpackDirectory(Path parent) { + int attempts = 0; + while (attempts++ < 1000) { + String fileName = Paths.get(this.jarFile.getName()).getFileName().toString(); + Path unpackDirectory = parent.resolve(fileName + "-spring-boot-libs-" + UUID.randomUUID()); + try { + createDirectory(unpackDirectory); + return unpackDirectory; + } catch (IOException ex) { + // ignore + } + } + throw new IllegalStateException("Failed to create unpack directory in directory '" + parent + "'"); + } + + private void unpack(JarEntry entry, Path path) throws IOException { + createFile(path); + path.toFile().deleteOnExit(); + try (InputStream inputStream = this.jarFile.getInputStream(entry); + OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING)) { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + } + } + + private void createDirectory(Path path) throws IOException { + Files.createDirectory(path, getFileAttributes(path.getFileSystem(), DIRECTORY_PERMISSIONS)); + } + + private void createFile(Path path) throws IOException { + Files.createFile(path, getFileAttributes(path.getFileSystem(), FILE_PERMISSIONS)); + } + + private FileAttribute[] getFileAttributes(FileSystem fileSystem, EnumSet ownerReadWrite) { + if (!fileSystem.supportedFileAttributeViews().contains("posix")) { + return NO_FILE_ATTRIBUTES; + } + return new FileAttribute[] { PosixFilePermissions.asFileAttribute(ownerReadWrite) }; + } + + @Override + public String toString() { + try { + return getUrl().toString(); + } catch (Exception ex) { + return "jar archive"; + } + } + + /** + * Abstract base class for iterator implementations. + */ + private abstract static class AbstractIterator implements Iterator { + + private final Iterator iterator; + + private final EntryFilter searchFilter; + + private final EntryFilter includeFilter; + + private Entry current; + + AbstractIterator(Iterator iterator, EntryFilter searchFilter, EntryFilter includeFilter) { + this.iterator = iterator; + this.searchFilter = searchFilter; + this.includeFilter = includeFilter; + this.current = poll(); + } + + @Override + public boolean hasNext() { + return this.current != null; + } + + @Override + public T next() { + T result = adapt(this.current); + this.current = poll(); + return result; + } + + private Entry poll() { + while (this.iterator.hasNext()) { + JarFileEntry candidate = new JarFileEntry(this.iterator.next()); + if ((this.searchFilter == null || this.searchFilter.matches(candidate)) + && (this.includeFilter == null || this.includeFilter.matches(candidate))) { + return candidate; + } + } + return null; + } + + protected abstract T adapt(Entry entry); + + } + + /** + * {@link Archive.Entry} iterator implementation backed by {@link JarEntry}. + */ + private static class EntryIterator extends AbstractIterator { + + EntryIterator(Iterator iterator, EntryFilter searchFilter, EntryFilter includeFilter) { + super(iterator, searchFilter, includeFilter); + } + + @Override + protected Entry adapt(Entry entry) { + return entry; + } + + } + + /** + * Nested {@link Archive} iterator implementation backed by {@link JarEntry}. + */ + private class NestedArchiveIterator extends AbstractIterator { + + NestedArchiveIterator(Iterator iterator, EntryFilter searchFilter, EntryFilter includeFilter) { + super(iterator, searchFilter, includeFilter); + } + + @Override + protected Archive adapt(Entry entry) { + try { + return getNestedArchive(entry); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + } + + /** + * {@link Archive.Entry} implementation backed by a {@link JarEntry}. + */ + private static class JarFileEntry implements Entry { + + private final JarEntry jarEntry; + + JarFileEntry(JarEntry jarEntry) { + this.jarEntry = jarEntry; + } + + JarEntry getJarEntry() { + return this.jarEntry; + } + + @Override + public boolean isDirectory() { + return this.jarEntry.isDirectory(); + } + + @Override + public String getName() { + return this.jarEntry.getName(); + } + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/ClassLoaderTranslator.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/ClassLoaderTranslator.java new file mode 100755 index 0000000..9a24c09 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/ClassLoaderTranslator.java @@ -0,0 +1,127 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResource; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +/** + * classloader 转换器 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public class ClassLoaderTranslator implements ResourceLoaderFactory { + + private final URLClassLoader classLoader; + + public ClassLoaderTranslator(URLClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public void addResource(String path) throws Exception { + throw new RuntimeException("Does not support!"); + } + + @Override + public void addResource(File file) throws Exception { + throw new RuntimeException("Does not support!"); + } + + @Override + public void addResource(Path path) throws Exception { + throw new RuntimeException("Does not support!"); + } + + @Override + public void addResource(URL url) throws Exception { + throw new RuntimeException("Does not support!"); + } + + @Override + public void addResource(Resource resource) throws Exception { + throw new RuntimeException("Does not support!"); + } + + @Override + public void addResource(ResourceLoader resourceLoader) throws Exception { + throw new RuntimeException("Does not support!"); + } + + @Override + public Resource findFirstResource(String name) { + URL url = classLoader.getResource(name); + if(url == null){ + return null; + } + return new DefaultResource(name, url, url); + } + + @Override + public Enumeration findAllResource(String name) { + try { + Enumeration resources = classLoader.getResources(name); + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return resources.hasMoreElements(); + } + + @Override + public Resource nextElement() { + URL url = resources.nextElement(); + if(url == null){ + return null; + } + return new DefaultResource(name, url, url); + } + }; + } catch (IOException e) { + return Collections.emptyEnumeration(); + } + } + + @Override + public InputStream getInputStream(String name) { + return classLoader.getResourceAsStream(name); + } + + @Override + public List getUrls() { + return Arrays.asList(classLoader.getURLs()); + } + + @Override + public void close() throws Exception { + + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GeneralUrlClassLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GeneralUrlClassLoader.java new file mode 100755 index 0000000..8fcd39e --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GeneralUrlClassLoader.java @@ -0,0 +1,111 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.storage.EmptyResourceStorage; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.List; + +/** + * 通用的Url ClassLoader + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class GeneralUrlClassLoader extends URLClassLoader implements ResourceLoaderFactory { + + private final String name; + private final ResourceLoaderFactory classLoaderTranslator; + + private final ResourceStorage resourceStorage = new EmptyResourceStorage(); + + public GeneralUrlClassLoader(String name, ClassLoader parent) { + super(new URL[]{}, parent); + this.name = name; + this.classLoaderTranslator = new ClassLoaderTranslator(this); + } + + public String getName() { + return name; + } + + @Override + public void addResource(String path) throws Exception { + addResource(Paths.get(path)); + } + + @Override + public void addResource(File file) throws Exception { + if(!file.exists()){ + throw new FileNotFoundException("Not found file:" + file.getPath()); + } + addURL(file.toPath().toUri().toURL()); + } + + @Override + public void addResource(Path path) throws Exception { + addResource(path.toFile()); + } + + @Override + public void addResource(URL url) throws Exception { + super.addURL(url); + } + + @Override + public void addResource(Resource resource) throws Exception { + addResource(resource.getUrl()); + } + + @Override + public void addResource(ResourceLoader resourceLoader) throws Exception { + resourceLoader.load(resourceStorage); + } + + @Override + public Resource findFirstResource(String name) { + return classLoaderTranslator.findFirstResource(name); + } + + @Override + public Enumeration findAllResource(String name) { + return classLoaderTranslator.findAllResource(name); + } + + @Override + public InputStream getInputStream(String name) { + return classLoaderTranslator.getInputStream(name); + } + + @Override + public List getUrls() { + return classLoaderTranslator.getUrls(); + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java new file mode 100755 index 0000000..4108f54 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/GenericClassLoader.java @@ -0,0 +1,340 @@ +/** + * Copyright [2019-Present] [starBlues] + *

+ * Licensed 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 com.gitee.starblues.loader.classloader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.utils.Assert; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.*; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 基本的 ClassLoader + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class GenericClassLoader extends URLClassLoader implements ResourceLoaderFactory { + + private final String name; + private final ClassLoader parent; + + protected final ResourceLoaderFactory resourceLoaderFactory; + + private final ResourceLoaderFactory classLoaderTranslator; + + private final Map> pluginClassCache = new ConcurrentHashMap<>(); + + public GenericClassLoader(String name, ResourceLoaderFactory resourceLoaderFactory) { + this(name, null, resourceLoaderFactory); + } + + public GenericClassLoader(String name, ClassLoader parent, ResourceLoaderFactory resourceLoaderFactory) { + super(new URL[]{}, null); + this.name = Assert.isNotEmpty(name, "name 不能为空"); + this.resourceLoaderFactory = Assert.isNotNull(resourceLoaderFactory, "resourceLoaderFactory 不能为空"); + this.parent = parent; + this.classLoaderTranslator = new ClassLoaderTranslator(this); + } + + public String getName() { + return name; + } + + public ClassLoader getParentClassLoader() { + return parent; + } + + @Override + public void addResource(String path) throws Exception { + resourceLoaderFactory.addResource(path); + } + + @Override + public void addResource(File file) throws Exception { + resourceLoaderFactory.addResource(file); + } + + @Override + public void addResource(Path path) throws Exception { + resourceLoaderFactory.addResource(path); + } + + @Override + public void addResource(URL url) throws Exception { + resourceLoaderFactory.addResource(url); + } + + @Override + public void addResource(Resource resource) throws Exception { + resourceLoaderFactory.addResource(resource); + } + + @Override + public void addResource(ResourceLoader resourceLoader) throws Exception { + resourceLoaderFactory.addResource(resourceLoader); + } + + @Override + public Resource findFirstResource(String name) { + return classLoaderTranslator.findFirstResource(name); + } + + @Override + public Enumeration findAllResource(String name) { + return classLoaderTranslator.findAllResource(name); + } + + @Override + public InputStream getInputStream(String name) { + return classLoaderTranslator.getInputStream(name); + } + + @Override + public List getUrls() { + return classLoaderTranslator.getUrls(); + } + + @Override + public Class loadClass(String className) throws ClassNotFoundException { + synchronized (getClassLoadingLock(className)) { + return findClass(className); + } + } + + @Override + public URL[] getURLs() { + List urlList = resourceLoaderFactory.getUrls(); + URL[] urls = new URL[urlList.size()]; + for (int i = 0; i < urlList.size(); i++) { + urls[i] = urlList.get(i); + } + return urls; + } + + @Override + public InputStream getResourceAsStream(String name) { + name = formatResourceName(name); + InputStream inputStream = findInputStreamFromParent(name); + if (inputStream != null) { + return inputStream; + } + return findInputStreamFromLocal(name); + } + + @Override + public URL getResource(String name) { + name = formatResourceName(name); + URL url = findResourceFromParent(name); + if (url != null) { + return url; + } + return findResourceFromLocal(name); + } + + @Override + public Enumeration getResources(String name) throws IOException { + name = formatResourceName(name); + Enumeration parentResources = findResourcesFromParent(name); + Enumeration localResources = findResourcesFromLocal(name); + return new Enumeration() { + + private int index = 0; + + @Override + public boolean hasMoreElements() { + if (parentResources != null && parentResources.hasMoreElements()) { + return true; + } + index = 1; + return localResources.hasMoreElements(); + } + + @Override + public URL nextElement() { + if (index == 0) { + return parentResources.nextElement(); + } else { + return localResources.nextElement(); + } + } + }; + } + + @Override + public void close() throws IOException { + super.close(); + IOUtils.closeQuietly(resourceLoaderFactory); + } + + @Override + public void release() { + ResourceUtils.release(resourceLoaderFactory); + } + + @Override + protected Class findClass(String className) throws ClassNotFoundException { + Class loadedClass = findClassFromParent(className); + if (loadedClass != null) { + return loadedClass; + } + loadedClass = findLoadedClass(className); + if (loadedClass != null) { + return loadedClass; + } + try { + loadedClass = findClassFromLocal(className); + } catch (IOException e) { + throw new ClassNotFoundException("ClassLoader[" + name + "]:" + className); + } + if (loadedClass != null) { + return loadedClass; + } + throw new ClassNotFoundException("ClassLoader[" + name + "]:" + className); + } + + protected Class findClassFromParent(String className) throws ClassNotFoundException { + try { + if (parent != null) { + return parent.loadClass(className); + } + return null; + } catch (Exception e) { + return null; + } + } + + protected Class findClassFromLocal(String name) throws IOException { + Class aClass; + String formatClassName = formatClassName(name); + + aClass = pluginClassCache.get(formatClassName); + if (aClass != null) { + return aClass; + } + + Resource resource = resourceLoaderFactory.findFirstResource(formatClassName); + byte[] bytes = null; + if (resource != null) { + bytes = resource.getBytes(); + } + if (bytes == null || bytes.length == 0) { + bytes = getClassByte(formatClassName); + } + if (bytes == null || bytes.length == 0) { + return null; + } + aClass = super.defineClass(name, bytes, 0, bytes.length); + if (aClass == null) { + return null; + } + if (aClass.getPackage() == null) { + int lastDotIndex = name.lastIndexOf('.'); + String packageName = (lastDotIndex >= 0) ? name.substring(0, lastDotIndex) : ""; + super.definePackage(packageName, null, null, null, + null, null, null, null); + } + pluginClassCache.put(name, aClass); + return aClass; + } + + protected URL findResourceFromParent(String name) { + if (parent != null) { + return parent.getResource(name); + } + return null; + } + + protected URL findResourceFromLocal(String name) { + Resource resource = resourceLoaderFactory.findFirstResource(name); + if (resource == null) { + return null; + } + return resource.getUrl(); + } + + + protected InputStream findInputStreamFromParent(String name) { + if (parent != null) { + return parent.getResourceAsStream(name); + } + return null; + } + + protected InputStream findInputStreamFromLocal(String name) { + return resourceLoaderFactory.getInputStream(name); + } + + protected Enumeration findResourcesFromParent(String name) throws IOException { + if (parent != null) { + return parent.getResources(name); + } + return null; + } + + protected Enumeration findResourcesFromLocal(String name) throws IOException { + Enumeration enumeration = resourceLoaderFactory.findAllResource(name); + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return enumeration.hasMoreElements(); + } + + @Override + public URL nextElement() { + return enumeration.nextElement().getUrl(); + } + }; + } + + private byte[] getClassByte(String formatClassName) throws IOException { + try (InputStream inputStream = resourceLoaderFactory.getInputStream(formatClassName)) { + if (inputStream == null) { + return null; + } + try { + return IOUtils.read(inputStream); + } catch (Exception e) { + e.printStackTrace(); + return null; + } finally { + IOUtils.closeQuietly(inputStream); + } + } + } + + private String formatResourceName(String name) { + return ResourceUtils.formatStandardName(name); + } + + private String formatClassName(String className) { + className = className.replace('/', '~'); + className = className.replace('.', '/') + ".class"; + className = className.replace('~', '/'); + return className; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java new file mode 100755 index 0000000..06ca2ca --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/ExcludeResource.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.filter; + +import java.util.jar.JarEntry; + +/** + * 排除资源 + * @author starBlues + * @version 3.0.0 + */ +@FunctionalInterface +public interface ExcludeResource { + + /** + * 过滤排除 + * @param jarEntry jarEntry + * @return boolean + */ + boolean exclude(JarEntry jarEntry); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java new file mode 100755 index 0000000..5068fd1 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/filter/IncludeResource.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.filter; + +import java.util.jar.JarEntry; + +/** + * 包含资源 + * @author starBlues + * @version 3.0.0 + */ +@FunctionalInterface +public interface IncludeResource { + + /** + * 过滤排除 + * @param jarEntry jarEntry + * @return boolean + */ + boolean include(JarEntry jarEntry); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java new file mode 100755 index 0000000..283ed3d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/Resource.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource; + +import com.gitee.starblues.loader.utils.Release; + +import java.net.URL; + +/** + * 资源信息 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface Resource extends AutoCloseable, Release { + + String PACKAGE_SPLIT = "/"; + + /** + * 得到资源名称 + * @return 全局唯一的资源名称 + */ + String getName(); + + /** + * 得到根URL地址 + * @return URL + */ + URL getBaseUrl(); + + /** + * 得到完整URL地址 + * @return URL + */ + URL getUrl(); + + /** + * 得到资源字节数组 + * @return byte[] + */ + byte[] getBytes(); + + /** + * 解决byte + * @throws Exception 处理资源字节异常 + */ + void resolveByte() throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java new file mode 100755 index 0000000..beab785 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/ResourceByteGetter.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource; + +/** + * 资源 byte 得到者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface ResourceByteGetter { + + /** + * 得到 byte + * @return byte[] + * @throws Exception Exception + */ + byte[] get() throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Cache.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Cache.java new file mode 100755 index 0000000..6e0fd83 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Cache.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * 缓存接口 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public interface Cache { + + /** + * 缓存数据 + * @param key 缓存的key + * @param value 缓存的值 + */ + void put(K key, V value); + + /** + * 缓存大小 + * @return int + */ + int size(); + + /** + * 是否存在缓存 + * @param key 缓存key + * @return true: 存在, false: 不存在 + */ + boolean containsKey(K key); + + /** + * 获取缓存值 + * @param key 缓存的key + * @return 缓存值, 不存在返回 null + */ + V get(K key); + + /** + * 得到缓存值。如果不存在放默认的 + * @param key 缓存的key + * @param supplier 默认值 + * @param defaultAdded 如果不存在, 默认的释放添加进入缓存中 + * @return V + */ + V getOrDefault(K key, Supplier supplier, boolean defaultAdded); + + /** + * 移除缓存 + * @param key 缓存的key + * @return 移除的值 + */ + V remove(K key); + + /** + * 清理过期的缓存 + * @return 清除的个数 + */ + int cleanExpired(); + + /** + * 清除全部缓存 + */ + void clear(); + + /** + * 依次删除 + * @param consumer 消费 + */ + void clear(Consumer consumer); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheExpirationTrigger.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheExpirationTrigger.java new file mode 100755 index 0000000..6aa8855 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheExpirationTrigger.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + +import java.util.function.Supplier; + +/** + * 缓存过期调度接口 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public interface CacheExpirationTrigger { + + /** + * 添加缓存过期调度 + * @param key 缓存的key + * @param cache 缓存对象 + */ + void addCache(String key, Cache cache); + + /*** + * 获取缓存 + * @param key 缓存的key + * @param cacheSupplier 不存在时提供, 并add到缓存中 + * @return 缓存 + * @param K + * @param V + */ + Cache getCache(String key, Supplier> cacheSupplier); + + /** + * 移除缓存过期调度 + * @param key 缓存的key + */ + void removeCache(String key); + + /** + * 启动调度 + */ + void start(); + + /** + * 停止调度 + */ + void stop(); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheLinkedHashMap.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheLinkedHashMap.java new file mode 100755 index 0000000..df76398 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/CacheLinkedHashMap.java @@ -0,0 +1,54 @@ +package com.gitee.starblues.loader.classloader.resource.cache; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 注释 + * + * @author starBlues + * @since 1.0.0 + * @version 1.0.0 + */ +public class CacheLinkedHashMap extends LinkedHashMap { + + private final int size; + private final RemoveListener removeListener; + + public CacheLinkedHashMap(int size) { + this(size, null); + } + + public CacheLinkedHashMap(int size, RemoveListener removeListener) { + super(size + 1, 1.0f, true); + this.size = size; + this.removeListener = removeListener; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + if (size == 0) { + return false; + } + int removeSize = size() - size; + if(removeSize > 0){ + if(removeListener != null){ + try { + removeListener.remove(eldest); + } catch (Exception e){ + // 忽略 + } + } + return true; + } + return false; + } + + @FunctionalInterface + public interface RemoveListener{ + + void remove(Map.Entry eldest); + + } + +} \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/DefaultCacheExpirationTrigger.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/DefaultCacheExpirationTrigger.java new file mode 100755 index 0000000..5b5b5e7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/DefaultCacheExpirationTrigger.java @@ -0,0 +1,112 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +/** + * 默认缓存过期调度实现 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public class DefaultCacheExpirationTrigger implements CacheExpirationTrigger{ + + private final Map> cacheMap = new ConcurrentHashMap<>(); + + private ScheduledExecutorService scheduledExecutor; + + private static volatile DefaultCacheExpirationTrigger TRIGGER; + + private final long delay; + private final TimeUnit unit; + + private DefaultCacheExpirationTrigger(long delay, TimeUnit unit){ + this.delay = delay; + this.unit = unit; + start(); + } + + public static CacheExpirationTrigger getCacheExpirationTrigger(long delay, TimeUnit unit){ + if (TRIGGER == null){ + synchronized(DefaultCacheExpirationTrigger.class){ + TRIGGER = new DefaultCacheExpirationTrigger(delay, unit); + } + } + return TRIGGER; + } + + @Override + public synchronized void addCache(String key, Cache cache) { + if(cacheMap.containsKey(key)){ + throw new IllegalStateException(key + " already exists!"); + } + cacheMap.put(key, cache); + } + + @SuppressWarnings("unchecked") + @Override + public synchronized Cache getCache(String key, Supplier> cacheSupplier) { + Cache cache = cacheMap.get(key); + if(cache == null){ + Cache supplier = cacheSupplier.get(); + if(supplier == null){ + return null; + } + cacheMap.put(key, supplier); + return supplier; + } else { + return (Cache) cache; + } + } + + @Override + public synchronized void removeCache(String key) { + cacheMap.remove(key); + } + + @Override + public synchronized void start() { + scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + scheduledExecutor.scheduleWithFixedDelay(this::cleanUp, delay, delay, unit); + } + + @Override + public synchronized void stop() { + scheduledExecutor.shutdownNow(); + } + + private void cleanUp(){ + if(cacheMap.isEmpty()){ + return; + } + cacheMap.forEach((k,v)->{ + try { + v.cleanExpired(); + } catch (Exception e){ + // log.warn("Checking clean cache:{} Exceptions. {}", k, e.getMessage()); + } + }); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Entity.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Entity.java new file mode 100755 index 0000000..10eed19 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/Entity.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + +/** + * 缓存元素 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public class Entity { + + protected final V value; + protected final long ttl; + protected long lastAccessTimestamp; + + public Entity(V value, long ttl) { + this.value = value; + this.ttl = ttl; + + this.lastAccessTimestamp = System.currentTimeMillis(); + } + + public boolean isExpired() { + if (ttl == 0) { + return false; + } + return lastAccessTimestamp + ttl < System.currentTimeMillis(); + } + + public V getValue() { + lastAccessTimestamp = System.currentTimeMillis(); + return value; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMapCache.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMapCache.java new file mode 100755 index 0000000..e4ee7ed --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMapCache.java @@ -0,0 +1,227 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + +import java.util.Iterator; +import java.util.LinkedHashMap; + +import java.util.Map; +import java.util.concurrent.locks.StampedLock; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * LRU 缓存实现 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public class LRUMapCache implements Cache{ + + private final Map> cacheMap; + + private final StampedLock lock = new StampedLock(); + + private final int size; + private final long timeout; + + public LRUMapCache(int size, long timeout){ + this.size = size; + this.timeout = timeout; + this.cacheMap = new CacheLinkedHashMap>(size); + } + + @Override + public void put(K key, V value) { + long stamp = lock.writeLock(); + try { + Entity entity = new Entity<>(value, timeout); + if (isFull(key)) { + cleanExpired(false); + } + cacheMap.put(key, entity); + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public int size() { + long stamp = lock.tryOptimisticRead(); + int size = cacheMap.size(); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + size = cacheMap.size(); + } finally { + lock.unlockRead(stamp); + } + } + return size; + } + + @Override + public boolean containsKey(K key) { + return get(key) != null; + } + + @Override + public V get(K key) { + long stamp = lock.tryOptimisticRead(); + Entity entity = cacheMap.get(key); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + entity = cacheMap.get(key); + } finally { + lock.unlockRead(stamp); + } + } + if(entity != null){ + if(entity.isExpired()){ + remove(key); + return null; + } + return entity.getValue(); + } + return null; + } + + @Override + public V getOrDefault(K key, Supplier supplier, boolean defaultAdded) { + long stamp = lock.tryOptimisticRead(); + Entity entity = cacheMap.get(key); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + entity = cacheMap.get(key); + } finally { + lock.unlockRead(stamp); + } + } + if(entity != null){ + if(entity.isExpired()){ + remove(key); + } else { + return entity.getValue(); + } + } + + V v = supplier.get(); + if(v != null){ + if(defaultAdded){ + put(key, v); + } + } + return v; + } + + @Override + public V remove(K key) { + long stamp = lock.writeLock(); + try { + Entity cacheValue = cacheMap.remove(key); + if (cacheValue != null) { + return cacheValue.getValue(); + } else { + return null; + } + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public void clear() { + clear(null); + } + + @Override + public void clear(Consumer consumer) { + long stamp = lock.writeLock(); + try { + if(consumer == null){ + cacheMap.clear(); + return; + } + Iterator>> iterator = cacheMap.entrySet().iterator(); + while (iterator.hasNext()){ + try { + Map.Entry> entityEntry = iterator.next(); + Entity value = entityEntry.getValue(); + if(value == null){ + iterator.remove(); + continue; + } + V v = value.getValue(); + if(v == null){ + iterator.remove(); + continue; + } + consumer.accept(v); + } catch (Exception e){ + // 忽略 + } + } + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public int cleanExpired() { + return cleanExpired(true); + } + + private boolean isFull(K key) { + if (size == 0) { + return false; + } + if(cacheMap.size() < size){ + return false; + } + return !cacheMap.containsKey(key); + } + + public int cleanExpired(boolean isLock) { + if(!isLock){ + return actualCleanExpired(); + } + long stamp = lock.writeLock(); + try { + return actualCleanExpired(); + } finally { + lock.unlockWrite(stamp); + } + } + + private int actualCleanExpired(){ + Iterator>> cacheMapIterator = cacheMap.entrySet().iterator(); + int removeCount = 0; + while (cacheMapIterator.hasNext()){ + Map.Entry> entityMap = cacheMapIterator.next(); + Entity value = entityMap.getValue(); + if(value == null || value.isExpired()){ + cacheMapIterator.remove(); + removeCount++; + } + } + return removeCount; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapListCache.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapListCache.java new file mode 100755 index 0000000..f331106 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapListCache.java @@ -0,0 +1,289 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + +import com.gitee.starblues.loader.utils.ObjectUtils; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.StampedLock; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * LRU MultiMap List 缓存实现。list 集合中的元素单个过期 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +@Deprecated +public class LRUMultiMapListCache implements MultiCache{ + + private final Map>> cacheMap; + + private final StampedLock lock = new StampedLock(); + + private final int maxSize; + private final long timeout; + + private final AtomicInteger size = new AtomicInteger(0); + + public LRUMultiMapListCache(int maxSize, long timeout){ + this.maxSize = maxSize; + this.timeout = timeout; + this.cacheMap = new CacheLinkedHashMap>>(maxSize, entry->{ + List> value = entry.getValue(); + if(value != null){ + size.addAndGet(-value.size()); + } + }); + } + + @Override + public void put(K key, Collection value) { + if(ObjectUtils.isEmpty(value)){ + return; + } + long stamp = lock.writeLock(); + try { + List> entityAddedList = value.stream() + .map(v-> new Entity<>(v, timeout)) + .collect(Collectors.toList()); + if (isFull(key)) { + cleanExpired(false); + } + List> entityList = cacheMap.computeIfAbsent(key, k -> new ArrayList<>()); + entityList.addAll(entityAddedList); + size.addAndGet(value.size()); + } finally { + lock.unlockWrite(stamp); + } + } + + @SuppressWarnings("unchecked") + @Override + public void putSingle(K key, V value) { + put(key, ObjectUtils.toList(value)); + } + + @Override + public V getFirst(K key) { + long stamp = lock.tryOptimisticRead(); + List> entityList = cacheMap.get(key); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + entityList = cacheMap.get(key); + } finally { + lock.unlockRead(stamp); + } + } + if(ObjectUtils.isEmpty(entityList)){ + return null; + } + Iterator> iterator = entityList.iterator(); + while (iterator.hasNext()){ + Entity entity = iterator.next(); + if(entity.isExpired()){ + iterator.remove(); + size.addAndGet(-1); + } else { + return entity.getValue(); + } + } + return null; + } + + @Override + public int size() { + long stamp = lock.tryOptimisticRead(); + int s = size.get(); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + s = size.get(); + } finally { + lock.unlockRead(stamp); + } + } + return s; + } + + @Override + public boolean containsKey(K key) { + return get(key) != null; + } + + @Override + public Collection get(K key) { + long stamp = lock.tryOptimisticRead(); + List> entityList = cacheMap.get(key); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + entityList = cacheMap.get(key); + } finally { + lock.unlockRead(stamp); + } + } + if(ObjectUtils.isEmpty(entityList)){ + return null; + } + Iterator> iterator = entityList.iterator(); + List result = new ArrayList<>(entityList.size()); + while (iterator.hasNext()){ + Entity entity = iterator.next(); + if(entity.isExpired()){ + iterator.remove(); + size.addAndGet(-1); + } else { + result.add(entity.getValue()); + } + } + return result; + } + + @Override + public Collection getOrDefault(K key, Supplier> supplier, boolean defaultAdded) { + Collection collection = get(key); + if(!ObjectUtils.isEmpty(collection)){ + return collection; + } + Collection addCollection = supplier.get(); + if(ObjectUtils.isEmpty(addCollection)){ + return null; + } + if(defaultAdded){ + put(key, addCollection); + return addCollection; + } + return null; + } + + + + @Override + public Collection remove(K key) { + long stamp = lock.writeLock(); + try { + List> cacheValue = cacheMap.remove(key); + if (cacheValue == null) { + return null; + } + size.addAndGet(-cacheValue.size()); + return cacheValue.stream() + .map(Entity::getValue) + .collect(Collectors.toList()); + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public void clear() { + clear(null); + } + + @Override + public void clear(Consumer> consumer) { + long stamp = lock.writeLock(); + try { + if(consumer == null){ + cacheMap.clear(); + return; + } + Iterator>>> iterator = cacheMap.entrySet().iterator(); + while (iterator.hasNext()){ + try { + Map.Entry>> entityMap = iterator.next(); + List> entityList = entityMap.getValue(); + if(entityList == null){ + iterator.remove(); + continue; + } + List values = entityList.stream().map(Entity::getValue).collect(Collectors.toList()); + consumer.accept(values); + iterator.remove(); + } catch (Exception e){ + // 忽略 + } + } + } finally { + size.set(0); + lock.unlockWrite(stamp); + } + } + + @Override + public int cleanExpired() { + return cleanExpired(true); + } + + private boolean isFull(K key) { + if (maxSize == 0) { + return false; + } + if(size.get() < maxSize){ + return false; + } + return !cacheMap.containsKey(key); + } + + public int cleanExpired(boolean isLock) { + if(!isLock){ + return actualCleanExpired(); + } + long stamp = lock.writeLock(); + try { + return actualCleanExpired(); + } finally { + lock.unlockWrite(stamp); + } + } + + private int actualCleanExpired(){ + Iterator>>> cacheMapIterator = cacheMap.entrySet().iterator(); + int removeCount = 0; + while (cacheMapIterator.hasNext()){ + Map.Entry>> entityMap = cacheMapIterator.next(); + List> value = entityMap.getValue(); + if(ObjectUtils.isEmpty(value)){ + cacheMapIterator.remove(); + continue; + } + int valueSize = value.size(); + Iterator> entityIterator = value.iterator(); + int removeSize = 0; + while (entityIterator.hasNext()){ + Entity entity = entityIterator.next(); + if(entity == null || entity.isExpired()){ + entityIterator.remove(); + removeSize++; + } + } + if(valueSize == removeSize){ + cacheMapIterator.remove(); + } + size.addAndGet(-removeSize); + removeCount = removeCount + removeSize; + } + return removeCount; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapUnifiedListCache.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapUnifiedListCache.java new file mode 100755 index 0000000..6185ef6 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/LRUMultiMapUnifiedListCache.java @@ -0,0 +1,288 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + +import com.gitee.starblues.loader.utils.ObjectUtils; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.StampedLock; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * LRU MultiMap List 缓存实现。list 集合中的元素统一过期 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public class LRUMultiMapUnifiedListCache implements MultiCache{ + + private final Map cacheMap; + + private final StampedLock lock = new StampedLock(); + + private final int maxSize; + private final long timeout; + + private final AtomicInteger size = new AtomicInteger(0); + + public LRUMultiMapUnifiedListCache(int maxSize, long timeout){ + this.maxSize = maxSize; + this.timeout = timeout; + this.cacheMap = new CacheLinkedHashMap(maxSize, entry->{ + EntityList value = entry.getValue(); + if(value != null){ + size.addAndGet(-value.size()); + } else { + size.addAndGet(-1); + } + }); + } + + @Override + public void put(K key, Collection value) { + if(ObjectUtils.isEmpty(value)){ + return; + } + long stamp = lock.writeLock(); + try { + if (isFull(key)) { + cleanExpired(false); + } + EntityList entityList = cacheMap.computeIfAbsent(key, k -> new EntityList(timeout)); + entityList.add(value); + } finally { + lock.unlockWrite(stamp); + } + } + + @SuppressWarnings("unchecked") + @Override + public void putSingle(K key, V value) { + put(key, ObjectUtils.toList(value)); + } + + @Override + public V getFirst(K key) { + Collection collection = get(key); + return ObjectUtils.getFirst(collection); + } + + @Override + public int size() { + long stamp = lock.tryOptimisticRead(); + int s = size.get(); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + s = size.get(); + } finally { + lock.unlockRead(stamp); + } + } + return s; + } + + @Override + public boolean containsKey(K key) { + return get(key) != null; + } + + @Override + public Collection get(K key) { + long stamp = lock.tryOptimisticRead(); + EntityList entityList = cacheMap.get(key); + if(!lock.validate(stamp)){ + stamp = lock.readLock(); + try { + entityList = cacheMap.get(key); + } finally { + lock.unlockRead(stamp); + } + } + if(entityList == null){ + return null; + } + if(entityList.isExpired()){ + remove(key); + return null; + } + return Collections.unmodifiableList(entityList.getValue()); + } + + @Override + public Collection getOrDefault(K key, Supplier> supplier, boolean defaultAdded) { + Collection collection = get(key); + if(!ObjectUtils.isEmpty(collection)){ + return collection; + } + Collection addCollection = supplier.get(); + if(ObjectUtils.isEmpty(addCollection)){ + return null; + } + if(defaultAdded){ + put(key, addCollection); + return addCollection; + } + return null; + } + + @Override + public Collection remove(K key) { + long stamp = lock.writeLock(); + try { + EntityList cacheValue = cacheMap.remove(key); + if (cacheValue == null) { + return null; + } + size.addAndGet(-cacheValue.size()); + return cacheValue.getValue(); + } finally { + lock.unlockWrite(stamp); + } + } + + @Override + public void clear() { + clear(null); + } + + @Override + public void clear(Consumer> consumer) { + long stamp = lock.writeLock(); + try { + if(consumer == null){ + cacheMap.clear(); + return; + } + Iterator> iterator = cacheMap.entrySet().iterator(); + while (iterator.hasNext()){ + try { + Map.Entry entityMap = iterator.next(); + EntityList entityList = entityMap.getValue(); + if(entityList == null){ + iterator.remove(); + continue; + } + List value = entityList.getValue(); + if(value != null){ + consumer.accept(value); + } + iterator.remove(); + } catch (Exception e){ + // 忽略 + } + } + } finally { + size.set(0); + lock.unlockWrite(stamp); + } + } + + @Override + public int cleanExpired() { + return cleanExpired(true); + } + + private boolean isFull(K key) { + if (maxSize == 0) { + return false; + } + if(cacheMap.size() < maxSize){ + return false; + } + return !cacheMap.containsKey(key); + } + + public int cleanExpired(boolean isLock) { + if(!isLock){ + return actualCleanExpired(); + } + long stamp = lock.writeLock(); + try { + return actualCleanExpired(); + } finally { + lock.unlockWrite(stamp); + } + } + + private int actualCleanExpired(){ + Iterator> cacheMapIterator = cacheMap.entrySet().iterator(); + int removeCount = 0; + while (cacheMapIterator.hasNext()){ + Map.Entry entityMap = cacheMapIterator.next(); + EntityList value = entityMap.getValue(); + if(value == null){ + cacheMapIterator.remove(); + } else { + if(value.isExpired()){ + int valueSize = value.size(); + cacheMapIterator.remove(); + size.addAndGet(-valueSize); + removeCount = removeCount + valueSize; + } + } + } + return removeCount; + } + + private class EntityList extends Entity> implements AutoCloseable{ + + public EntityList(long ttl) { + super(new ArrayList<>(), ttl); + } + + public EntityList(List list, long ttl) { + super(list, ttl); + } + + public synchronized void add(V v){ + getValue().add(v); + size.addAndGet(1); + } + + public synchronized void add(Collection list){ + if(ObjectUtils.isEmpty(list)){ + return; + } + getValue().addAll(list); + size.addAndGet(list.size()); + } + + public synchronized V getFirst(){ + List list = getValue(); + if(ObjectUtils.isEmpty(list)){ + return null; + } + return list.get(0); + } + + public synchronized int size(){ + return getValue().size(); + } + + @Override + public synchronized void close() throws Exception { + List value = getValue(); + size.addAndGet(-value.size()); + value.clear(); + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/MultiCache.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/MultiCache.java new file mode 100755 index 0000000..6529323 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/cache/MultiCache.java @@ -0,0 +1,49 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.cache; + + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * 缓存接口 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public interface MultiCache extends Cache>{ + + /** + * put 一个值 + * @param key 缓存的key + * @param value 缓存的值 + */ + void putSingle(K key, V value); + + /** + * 得到第一个value + * @param key 缓存的key + * @return 第一个value + */ + V getFirst(K key); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java new file mode 100755 index 0000000..68a5fa5 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/AbstractResourceLoader.java @@ -0,0 +1,98 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.InputStream; +import java.net.URL; + +/** + * 抽象的资源加载者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public abstract class AbstractResourceLoader implements ResourceLoader{ + + private static final String CLASS_FILE_EXTENSION = ".class"; + + private boolean loaded = false; + + protected final URL baseUrl; + + protected AbstractResourceLoader(URL baseUrl) { + this.baseUrl = baseUrl; + } + + @Override + public URL getBaseUrl() { + return baseUrl; + } + + /** + * 初始化 resource + * @throws Exception 初始异常 + */ + @Override + public final synchronized void load(ResourceStorage resourceStorage) throws Exception{ + if(loaded){ + throw new Exception(this.getClass().getName()+": 已经初始化了, 不能再初始化!"); + } + try { + // 添加root 资源 + resourceStorage.add(new DefaultResource("/", baseUrl, baseUrl)); + loadOfChild(resourceStorage); + } finally { + loaded = true; + } + } + + /** + * 子类初始化实现 + * @param resourceStorage 资源存储 + * @throws Exception 初始异常 + */ + protected abstract void loadOfChild(ResourceStorage resourceStorage) throws Exception; + + protected byte[] getClassBytes(String path, InputStream inputStream, boolean isClose) throws Exception{ + if(!isClass(path)){ + return null; + } + try { + return IOUtils.read(inputStream); + } finally { + if(isClose){ + IOUtils.closeQuietly(inputStream); + } + } + } + + @Override + public void close() throws Exception { + + } + + private static boolean isClass(String path){ + if(path == null || "".equals(path)){ + return false; + } + return path.toLowerCase().endsWith(CLASS_FILE_EXTENSION); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/BaseURLResourceLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/BaseURLResourceLoader.java new file mode 100755 index 0000000..5468cb8 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/BaseURLResourceLoader.java @@ -0,0 +1,36 @@ +package com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.net.URL; + +/** + * 基本 URL 资源加载 + * + * @author starBlues + * @since 3.1.2 + * @version 3.1.2 + */ +public class BaseURLResourceLoader implements ResourceLoader{ + + private final URL baseUrl; + + public BaseURLResourceLoader(URL baseUrl) { + this.baseUrl = baseUrl; + } + + @Override + public URL getBaseUrl() { + return baseUrl; + } + + @Override + public void load(ResourceStorage resourceStorage) throws Exception { + + } + + @Override + public void close() throws Exception { + + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/CacheResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/CacheResource.java new file mode 100755 index 0000000..e042678 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/CacheResource.java @@ -0,0 +1,71 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.ResourceByteGetter; + +import java.net.URL; +import java.util.Arrays; + +/** + * 可缓存的资源 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public class CacheResource extends DefaultResource { + + protected byte[] bytes; + private ResourceByteGetter byteGetter; + + public CacheResource(String name, URL baseUrl, URL url) { + this(name, baseUrl, url, null); + } + + public CacheResource(String name, URL baseUrl, URL url, ResourceByteGetter byteGetter) { + super(name, baseUrl, url); + setBytes(byteGetter); + } + + public void setBytes(ResourceByteGetter byteGetter) { + this.byteGetter = byteGetter; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public void resolveByte() throws Exception{ + if(byteGetter != null){ + bytes = byteGetter.get(); + } + } + + @Override + public void close() throws Exception { + release(); + } + + @Override + public void release() { + Arrays.fill(bytes, (byte) 0); + bytes = null; + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java new file mode 100755 index 0000000..3ba8b84 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ClassPathLoader.java @@ -0,0 +1,102 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/** + * classpath 资源加载者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class ClassPathLoader extends AbstractResourceLoader { + + private final URL url; + + public ClassPathLoader(URL url) { + super(url); + this.url = Objects.requireNonNull(url, "url 不能为空"); + } + + public ClassPathLoader(File file) throws MalformedURLException { + this(file.toPath()); + } + + public ClassPathLoader(Path path) throws MalformedURLException { + super(path.toUri().toURL()); + this.url = super.baseUrl; + } + + @Override + protected void loadOfChild(ResourceStorage resourceStorage) throws Exception { + File file = new File(url.toURI()); + load(resourceStorage, file, null); + } + + private void load(ResourceStorage resourceStorage, File file, String currentPackageName) throws Exception { + if(currentPackageName == null){ + // 根目录 + currentPackageName = ""; + } else { + if("".equals(currentPackageName)){ + currentPackageName = file.getName(); + } else { + currentPackageName = currentPackageName + Resource.PACKAGE_SPLIT + file.getName(); + } + loadResource(resourceStorage, file, currentPackageName); + } + if(file.isDirectory()){ + File[] listFiles = file.listFiles(); + if(listFiles == null || listFiles.length == 0){ + return; + } + for (File subFile : listFiles) { + load(resourceStorage, subFile, currentPackageName); + } + } + } + + private void loadResource(ResourceStorage resourceStorage, File file, String packageName) throws Exception{ + if(file.isDirectory()){ + addResource(resourceStorage, file, packageName + Resource.PACKAGE_SPLIT); + } else { + addResource(resourceStorage, file, packageName); + } + } + + private void addResource(ResourceStorage resourceStorage, File file, String packageName) throws Exception { + CacheResource cacheResource = new CacheResource(packageName, url, new URL(url.toString() + packageName)); + cacheResource.setBytes(()->{ + if(file.exists() && file.isFile()){ + return getClassBytes(file.getPath(), Files.newInputStream(file.toPath()), true); + } else { + return null; + } + }); + resourceStorage.add(cacheResource); + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java new file mode 100755 index 0000000..3eeebe8 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResource.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.Resource; + +import java.net.URL; + +/** + * 默认的资源信息 + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class DefaultResource implements Resource { + + private final String name; + private final URL baseUrl; + private final URL url; + + public DefaultResource(String name, URL baseUrl, URL url) { + this.name = name; + this.baseUrl = baseUrl; + this.url = url; + } + + @Override + public String getName() { + return name; + } + + @Override + public URL getBaseUrl() { + return baseUrl; + } + + @Override + public URL getUrl() { + return url; + } + + @Override + public byte[] getBytes() { + return null; + } + + @Override + public void resolveByte() throws Exception{ + + } + + @Override + public void close() throws Exception { + + } + + @Override + public void release() { + + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java new file mode 100755 index 0000000..87a051d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/DefaultResourceLoaderFactory.java @@ -0,0 +1,137 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.loader.launcher.isolation.ResourceLoaderFactoryGetter; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +/** + * 默认的资源加载工厂 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class DefaultResourceLoaderFactory implements ResourceLoaderFactory{ + + private final ResourceStorage resourceStorage; + + public DefaultResourceLoaderFactory(String classLoaderName) { + resourceStorage = ResourceLoaderFactoryGetter.getResourceStorage(classLoaderName); + } + + @Override + public void addResource(String path) throws Exception{ + if(path == null || "".equals(path)){ + return; + } + addResource(Paths.get(path)); + } + + @Override + public void addResource(File file) throws Exception{ + if(file == null){ + return; + } + addResource(file.toPath()); + } + + @Override + public void addResource(Path path) throws Exception{ + if(path == null){ + return; + } + if(!Files.exists(path)){ + return; + } + addResource(path.toUri().toURL()); + } + + @Override + public void addResource(URL url) throws Exception{ + AbstractResourceLoader resourceLoader = null; + if(ResourceUtils.isJarFileUrl(url)) { + if(ResourceUtils.isJarProtocolUrl(url)){ + resourceLoader = new JarResourceLoader(url); + } else { + resourceLoader = new JarResourceLoader(Paths.get(url.toURI()).toFile()); + } + } else if(ResourceUtils.isZipFileUrl(url)){ + resourceLoader = new JarResourceLoader(Paths.get(url.toURI()).toFile()); + } else if(ResourceUtils.isFileUrl(url)){ + resourceLoader = new ClassPathLoader(url); + } + if(resourceLoader != null){ + addResource(resourceLoader); + } + } + + @Override + public void addResource(Resource resource) throws Exception { + resourceStorage.addBaseUrl(resource.getBaseUrl()); + resourceStorage.add(resource); + } + + @Override + public synchronized void addResource(ResourceLoader resourceLoader) throws Exception { + if(resourceLoader == null){ + return; + } + resourceStorage.addBaseUrl(resourceLoader.getBaseUrl()); + resourceLoader.load(resourceStorage); + } + + @Override + public Resource findFirstResource(String name) { + return resourceStorage.getFirst(name); + } + + @Override + public Enumeration findAllResource(String name) { + return resourceStorage.get(name); + } + + @Override + public InputStream getInputStream(String name) { + return resourceStorage.getFirstInputStream(name); + } + + @Override + public List getUrls() { + return resourceStorage.getBaseUrl(); + } + + @Override + public void close() throws Exception { + resourceStorage.close(); + } + + @Override + public void release() throws Exception{ + resourceStorage.release(); + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java new file mode 100755 index 0000000..69d374b --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/JarResourceLoader.java @@ -0,0 +1,106 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.filter.ExcludeResource; +import com.gitee.starblues.loader.classloader.filter.IncludeResource; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.io.File; +import java.net.URL; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * jar 资源加载者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class JarResourceLoader extends AbstractResourceLoader { + + private JarInputStream jarInputStream; + + private ExcludeResource excludeResource = (jarEntry)->false; + private IncludeResource includeResource = (jarEntry)->true; + + public JarResourceLoader(File file) throws Exception{ + super(new URL("jar:" + file.toURI().toURL() + "!/")); + URL url = file.toURI().toURL(); + this.jarInputStream = new JarInputStream(url.openStream()); + } + + public JarResourceLoader(URL url) throws Exception{ + super(url); + this.jarInputStream = new JarInputStream(url.openStream()); + } + + public JarResourceLoader(URL url, JarInputStream jarInputStream) throws Exception{ + super(url); + this.jarInputStream = jarInputStream; + } + + public void setExcludeResource(ExcludeResource excludeResource) { + if(excludeResource == null){ + return; + } + this.excludeResource = excludeResource; + } + + public void setIncludeResource(IncludeResource includeResource) { + if(includeResource == null){ + return; + } + this.includeResource = includeResource; + } + + @Override + protected void loadOfChild(ResourceStorage resourceStorage) throws Exception { + // 解析 + try { + JarEntry jarEntry = null; + while ((jarEntry = jarInputStream.getNextJarEntry()) != null) { + if(excludeResource.exclude(jarEntry)){ + jarInputStream.closeEntry(); + continue; + } + if(includeResource.include(jarEntry)){ + String name = resolveName(jarEntry.getName()); + URL url = new URL(baseUrl.toString() + name); + CacheResource cacheResource = new CacheResource(name, baseUrl, url, ()->{ + return getClassBytes(name, jarInputStream, false); + }); + resourceStorage.add(cacheResource); + } + jarInputStream.closeEntry(); + } + } finally { + jarInputStream.close(); + } + } + + @Override + public void close() throws Exception { + super.close(); + jarInputStream = null; + } + + protected String resolveName(String name){ + return name; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/MainJarResourceLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/MainJarResourceLoader.java new file mode 100755 index 0000000..f9330de --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/MainJarResourceLoader.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import java.net.URL; + +/** + * 主程序 jar 启动时资源加载者 + * + * @author starBlues + * @version 3.0.2 + */ +public class MainJarResourceLoader extends JarResourceLoader { + + private static final String PROD_CLASSES_PATH = "classes/"; + + public MainJarResourceLoader(URL url) throws Exception { + super(url); + } + + @Override + protected String resolveName(String name) { + return name.replace(PROD_CLASSES_PATH, ""); + } +} \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java new file mode 100755 index 0000000..a5262ad --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoader.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; + +import java.net.URL; + +/** + * 资源加载者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface ResourceLoader extends AutoCloseable{ + + /** + * 获取资源基本 URL + * @return URL + */ + URL getBaseUrl(); + + + /** + * 装载资源到ResourceStorage + * @param resourceStorage 资源存储 + * @throws Exception 装载异常 + */ + void load(ResourceStorage resourceStorage) throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java new file mode 100755 index 0000000..2036861 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/loader/ResourceLoaderFactory.java @@ -0,0 +1,108 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.loader; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.utils.Release; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.List; + +/** + * 资源加载工厂 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public interface ResourceLoaderFactory extends AutoCloseable, Release { + + /** + * 根据路径字符串添加资源 + * @param path 路径字符串 + * @throws Exception 添加资源异常 + */ + void addResource(String path) throws Exception; + + /** + * 根据文件添加资源 + * @param file 文件 + * @throws Exception 添加资源异常 + */ + void addResource(File file) throws Exception; + + /** + * 根据路径添加资源 + * @param path 路径 + * @throws Exception 添加资源异常 + */ + void addResource(Path path) throws Exception; + + /** + * 根据 URL 添加资源 + * @param url URL + * @throws Exception 添加资源异常 + */ + void addResource(URL url) throws Exception; + + /** + * 根据 Resource 添加 + * @param resource 资源 + * @throws Exception 添加资源异常 + */ + void addResource(Resource resource) throws Exception; + + /** + * 根据资源加载器添加资源 + * @param resourceLoader 资源加载者 + * @throws Exception 添加资源异常 + */ + void addResource(ResourceLoader resourceLoader) throws Exception; + + /** + * 根据资源名称获取第一个资源 + * @param name 资源名称 + * @return Resource + */ + Resource findFirstResource(String name); + + /** + * 根据资源名称获取资源集合 + * @param name 资源名称 + * @return Resource + */ + Enumeration findAllResource(String name); + + /** + * 根据资源名称获取第一个资源的 InputStream + * @param name 资源名称 + * @return Resource + */ + InputStream getInputStream(String name); + + /** + * 获取所有URL + * @return URL + */ + List getUrls(); + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/AbstractResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/AbstractResourceStorage.java new file mode 100755 index 0000000..f915df5 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/AbstractResourceStorage.java @@ -0,0 +1,252 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResource; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.loader.utils.ObjectUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + +/** + * 抽象的资源存储 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public abstract class AbstractResourceStorage implements ResourceStorage { + + private final Set baseUrls = new HashSet<>(); + private final ArrayDeque hotUrls = new ArrayDeque<>(); + private final List inputStreams = new ArrayList<>(); + + @Override + public void addBaseUrl(URL url){ + if(url == null){ + return; + } + synchronized (baseUrls){ + baseUrls.add(url); + } + } + + @Override + public List getBaseUrl(){ + synchronized (baseUrls){ + return Collections.unmodifiableList(new ArrayList<>(baseUrls)); + } + } + + + @Override + public final void add(Resource resource) throws Exception { + addResource(resource); + } + + @Override + public void close() throws Exception { + for (InputStream inputStream : inputStreams) { + IOUtils.closeQuietly(inputStream); + } + inputStreams.clear(); + hotUrls.clear(); + baseUrls.clear(); + } + + /** + * 子类添加资源 + * @param resource 资源 + * @throws Exception 添加移除 + */ + protected abstract void addResource(Resource resource) throws Exception; + + /** + * 格式化资源名称 + * @param name 资源名称 + * @return String + */ + protected final String formatResourceName(String name) { + return ResourceUtils.formatStandardName(name); + } + + protected final InputStream openStream(Resource resource){ + if(resource == null){ + return null; + } + try { + InputStream inputStream = resource.getUrl().openStream(); + inputStreams.add(inputStream); + return inputStream; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + protected final Enumeration openStream(Enumeration resources){ + if(resources == null){ + return Collections.emptyEnumeration(); + } + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return resources.hasMoreElements(); + } + + @Override + public InputStream nextElement() { + Resource resource = resources.nextElement(); + return openStream(resource); + } + }; + } + + protected final void closeResources(List resources){ + if(ObjectUtils.isEmpty(resources)){ + return; + } + for (Resource resource : resources) { + IOUtils.closeQuietly(resource); + } + resources.clear(); + } + + protected final synchronized Resource searchResource(String name) { + Set searchUrl = new HashSet<>(); + URL matchBaseUrl = null; + URL matchExistUrl = null; + // TODO 还需优化 + while (true){ + URL baseUrl = hotUrls.pollFirst(); + if(baseUrl == null){ + break; + } + searchUrl.add(baseUrl); + URL existUrl = ResourceUtils.getExistUrl(baseUrl, name); + if(existUrl != null){ + matchBaseUrl = baseUrl; + matchExistUrl = existUrl; + hotUrls.addFirst(baseUrl); + break; + } + } + + if(matchBaseUrl == null){ + List baseUrls = getBaseUrl(); + for (URL baseUrl : baseUrls) { + if(searchUrl.contains(baseUrl)){ + continue; + } + URL existUrl = ResourceUtils.getExistUrl(baseUrl, name); + if(existUrl != null){ + matchBaseUrl = baseUrl; + matchExistUrl = existUrl; + hotUrls.addFirst(baseUrl); + break; + } + } + } + if(matchBaseUrl != null){ + try { + Resource resource = new DefaultResource(name, matchBaseUrl, matchExistUrl); + addResource(resource); + return resource; + } catch (Exception e) { + return null; + } + } + return null; + } + + protected final Enumeration searchResources(String name){ + List baseUrls = getBaseUrl(); + return new InternalEnumeration(baseUrls, name); + } + + + private class InternalEnumeration implements Enumeration{ + + private final List baseUrls; + private final String name; + + private int index = 0; + private Resource resource = null; + + private InternalEnumeration(List baseUrls, String name) { + this.baseUrls = baseUrls; + this.name = name; + } + + @Override + public boolean hasMoreElements() { + return next(); + } + @Override + public Resource nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + Resource r = resource; + resource = null; + return r; + } + + private boolean next() { + if (resource != null) { + return true; + } else { + URL baseUrl; + while (index < baseUrls.size()){ + baseUrl = baseUrls.get(index++); + resource = getResource(baseUrl); + if(resource != null){ + return true; + } + } + return false; + } + } + + private Resource getResource(URL baseUrl){ + URL existUrl = ResourceUtils.getExistUrl(baseUrl, name); + if(existUrl == null){ + return null; + } + Resource resource = new DefaultResource(name, baseUrl, existUrl); + addResourceWrapper(resource); + return resource; + } + } + + private void addResourceWrapper(Resource resource){ + if(resource == null){ + return; + } + try { + addResource(resource); + } catch (Exception e){ + // 忽略异常 + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheFastResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheFastResourceStorage.java new file mode 100755 index 0000000..b6b162d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheFastResourceStorage.java @@ -0,0 +1,142 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.cache.*; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.loader.utils.ObjectUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.InputStream; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 快速且可缓存的资源存储者 + * 优点: 释放前速度比较快, 释放后可根据LRU缓存机制进行缓存 + * 缺点: 释放前占用内存比较高 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.2 + */ +public class CacheFastResourceStorage extends AbstractResourceStorage { + + protected final MultiCache resourceStorage; + private final ResourceStorage cacheResourceStorage; + + private volatile boolean release = false; + + @SuppressWarnings("all") + public CacheFastResourceStorage(String key) { + this.cacheResourceStorage = new CachePerpetualResourceStorage(); + // 最大 1000 个, 最长 3 分钟 + CacheExpirationTrigger trigger = DefaultCacheExpirationTrigger + .getCacheExpirationTrigger(3, TimeUnit.MINUTES); + resourceStorage = (MultiCache) trigger.getCache(key, + () -> new LRUMultiMapUnifiedListCache(1000, 180000)); + } + + @Override + protected void addResource(Resource resource) throws Exception { + if(!release){ + cacheResourceStorage.add(resource); + return; + } + resource.resolveByte(); + String name = formatResourceName(resource.getName()); + resourceStorage.putSingle(name, resource); + } + + @Override + public boolean exist(String name) { + if(!release){ + return cacheResourceStorage.exist(name); + } + return getFirst(name) != null; + } + + @Override + public Resource getFirst(String name) { + if(!release){ + return cacheResourceStorage.getFirst(name); + } + if(ObjectUtils.isEmpty(name)){ + return null; + } + name = formatResourceName(name); + Resource firstResource = resourceStorage.getFirst(name); + if(firstResource != null){ + return firstResource; + } + return searchResource(name); + } + + @Override + public Enumeration get(String name) { + if(!release){ + return cacheResourceStorage.get(name); + } + if(ObjectUtils.isEmpty(name)){ + return Collections.emptyEnumeration(); + } + name = formatResourceName(name); + Collection resources = resourceStorage.get(name); + if(!ObjectUtils.isEmpty(resources)){ + return Collections.enumeration(resources); + } + return searchResources(name); + } + + @Override + public InputStream getFirstInputStream(String name) { + Resource resource = getFirst(name); + return openStream(resource); + } + + @Override + public Enumeration getInputStream(String name) { + if(!release){ + return cacheResourceStorage.getInputStream(name); + } + Enumeration resources = get(name); + if(!ObjectUtils.isEmpty(resources)){ + return openStream(resources); + } + Enumeration searchResources = searchResources(name); + return openStream(searchResources); + } + + @Override + public synchronized void release() throws Exception { + if(!release){ + IOUtils.closeQuietly(cacheResourceStorage); + } + resourceStorage.cleanExpired(); + release = true; + } + + @Override + public void close() throws Exception { + resourceStorage.clear(ResourceUtils::release); + IOUtils.closeQuietly(cacheResourceStorage); + super.close(); + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheLowMemoryResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheLowMemoryResourceStorage.java new file mode 100755 index 0000000..df36f31 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CacheLowMemoryResourceStorage.java @@ -0,0 +1,149 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.cache.Cache; +import com.gitee.starblues.loader.classloader.resource.cache.DefaultCacheExpirationTrigger; +import com.gitee.starblues.loader.classloader.resource.cache.LRUMapCache; +import com.gitee.starblues.loader.utils.ObjectUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + +import java.io.InputStream; +import java.net.URL; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * 直接可缓存的资源存储, + * 优点: 释放前占用内存做了限制, 不会占用太高, 处于占用内存比较低, 且可根据LRU缓存机制进行缓存 + * 缺点: 如果缓存未命中, 速度比较慢 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + * @deprecated 暂时遗弃 + */ +@Deprecated +public class CacheLowMemoryResourceStorage extends AbstractResourceStorage { + + protected final Map>> resourceStorage = new ConcurrentHashMap<>(); + + public CacheLowMemoryResourceStorage(String key) { + } + + @Override + public void addBaseUrl(URL url) { + super.addBaseUrl(url); + String key = url.toString(); + if(!resourceStorage.containsKey(key)){ + Cache> cache = DefaultCacheExpirationTrigger.getCacheExpirationTrigger(3, TimeUnit.MINUTES) + .getCache(key, () -> new LRUMapCache>(10000, 180000)); + resourceStorage.put(key, cache); + } + } + + private Cache> getCache(URL baseUrl){ + String key = baseUrl.toString(); + Cache> cache = resourceStorage.get(key); + if(cache != null){ + return cache; + } + cache = DefaultCacheExpirationTrigger.getCacheExpirationTrigger(3, TimeUnit.MINUTES) + .getCache(key, () -> new LRUMapCache>(1000, 180000)); + resourceStorage.put(key, cache); + return cache; + } + + @Override + protected void addResource(Resource resource) throws Exception { + String name = formatResourceName(resource.getName()); + List resources = getCache(resource.getBaseUrl()).getOrDefault(name, ArrayList::new, true); + resources.add(resource); + } + + @Override + public boolean exist(String name) { + return get(name) != null; + } + + @Override + public Resource getFirst(String name) { + if(ObjectUtils.isEmpty(name)){ + return null; + } + name = formatResourceName(name); + for (Cache> cache : resourceStorage.values()) { + List resources = cache.get(name); + if(!ObjectUtils.isEmpty(resources)){ + return resources.get(0); + } + } + return searchResource(name); + } + + @Override + public Enumeration get(String name) { + List resources1 = new ArrayList<>(); + for (Cache> cache : resourceStorage.values()) { + List resources = cache.get(name); + if(!ObjectUtils.isEmpty(resources)){ + resources1.addAll(resources); + } + } + if(!resources1.isEmpty()){ + return Collections.enumeration(resources1); + } + return searchResources(name); + } + + @Override + public InputStream getFirstInputStream(String name) { + Resource resource = getFirst(name); + if(resource == null){ + return null; + } + return openStream(resource); + } + + @Override + public Enumeration getInputStream(String name) { + Enumeration resources = searchResources(name); + return openStream(resources); + } + + @Override + public synchronized void release() throws Exception { + for (Cache> value : resourceStorage.values()) { + value.clear(ResourceUtils::release); + } + resourceStorage.clear(); + } + + @Override + public void close() throws Exception { + //resourceStorage.clear(ResourceUtils::release); + super.close(); + release(); +// for (List resources : resourceStorage.values()) { +// ResourceUtils.release(resources); +// } + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CachePerpetualResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CachePerpetualResourceStorage.java new file mode 100755 index 0000000..bebd310 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/CachePerpetualResourceStorage.java @@ -0,0 +1,105 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.utils.ObjectUtils; +import java.io.InputStream; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 永久缓存 + * 优点: 速度快 + * 缺点: 占用内存很高 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class CachePerpetualResourceStorage extends AbstractResourceStorage { + + protected final Map> resourceStorage = new ConcurrentHashMap<>(); + + @Override + public void addResource(Resource resource) throws Exception { + resource.resolveByte(); + String name = formatResourceName(resource.getName()); + List resources = resourceStorage.computeIfAbsent(name, k -> new ArrayList<>()); + resources.add(resource); + } + + @Override + public boolean exist(String name) { + if(ObjectUtils.isEmpty(name)){ + return false; + } + name = formatResourceName(name); + return resourceStorage.containsKey(name); + } + + @Override + public Resource getFirst(String name) { + if(ObjectUtils.isEmpty(name)){ + return null; + } + name = formatResourceName(name); + List resources = resourceStorage.get(name); + if(ObjectUtils.isEmpty(resources)){ + return null; + } + return resources.get(0); + } + + @Override + public Enumeration get(String name) { + if(ObjectUtils.isEmpty(name)){ + return Collections.emptyEnumeration(); + } + name = formatResourceName(name); + List resources = resourceStorage.get(name); + if(ObjectUtils.isEmpty(resources)){ + return Collections.emptyEnumeration(); + } + return Collections.enumeration(resources); + } + + @Override + public InputStream getFirstInputStream(String name) { + Resource resource = getFirst(name); + return openStream(resource); + } + + @Override + public Enumeration getInputStream(String name) { + Enumeration resources = get(name); + if(ObjectUtils.isEmpty(resources)){ + return Collections.emptyEnumeration(); + } + return openStream(resources); + } + + @Override + public void close() throws Exception { + super.close(); + for (List resourceList : resourceStorage.values()) { + closeResources(resourceList); + } + resourceStorage.clear(); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/EmptyResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/EmptyResourceStorage.java new file mode 100755 index 0000000..2808931 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/EmptyResourceStorage.java @@ -0,0 +1,79 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; + +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; + +/** + * 空的资源存储 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.1 + */ +public class EmptyResourceStorage implements ResourceStorage{ + + @Override + public void addBaseUrl(URL baseUrl) { + + } + + @Override + public List getBaseUrl() { + return null; + } + + @Override + public void add(Resource resource) throws Exception { + + } + + @Override + public boolean exist(String name) { + return false; + } + + @Override + public Resource getFirst(String name) { + return null; + } + + @Override + public Enumeration get(String name) { + return null; + } + + @Override + public InputStream getFirstInputStream(String name) { + return null; + } + + @Override + public Enumeration getInputStream(String name) { + return null; + } + + @Override + public void close() throws Exception { + + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java new file mode 100755 index 0000000..aafd31d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/classloader/resource/storage/ResourceStorage.java @@ -0,0 +1,90 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.classloader.resource.storage; + +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.utils.Release; + +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; + +/** + * 资源存储者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public interface ResourceStorage extends AutoCloseable, Release { + + /** + * 添加根url + * @param baseUrl url + */ + void addBaseUrl(URL baseUrl); + + /** + * 获取根url + * @return url list + */ + List getBaseUrl(); + + /** + * 添加资源 + * @param resource 资源名称 + * @throws Exception 添加资源异常 + */ + void add(Resource resource) throws Exception; + + /** + * 存在资源 + * @param name 资源名称 + * @return 存在 true, 不存在 false + */ + boolean exist(String name); + + /** + * 获取第一个资源 + * @param name 资源名称 + * @return Resource + */ + Resource getFirst(String name); + + /** + * 获取所有资源 + * @param name 资源名称 + * @return Resource + */ + Enumeration get(String name); + + /** + * 获取第一个资源的 InputStream + * @param name 资源名称 + * @return Resource + */ + InputStream getFirstInputStream(String name); + + /** + * 获取所有资源的 InputStream + * @param name 资源名称 + * @return InputStream + */ + Enumeration getInputStream(String name); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java new file mode 100755 index 0000000..1e6f6f5 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AbstractJarFile.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.Permission; + +/** + * Base class for extended variants of {@link java.util.jar.JarFile}. + * + * @author Phillip Webb + */ +public abstract class AbstractJarFile extends java.util.jar.JarFile{ + + /** + * Create a new {@link AbstractJarFile}. + * @param file the root jar file. + * @throws IOException on IO error + */ + AbstractJarFile(File file) throws IOException { + super(file); + } + + /** + * Return a URL that can be used to access this JAR file. NOTE: the specified URL + * cannot be serialized and or cloned. + * @return the URL + * @throws MalformedURLException if the URL is malformed + */ + abstract URL getUrl() throws MalformedURLException; + + /** + * Return the {@link JarFileType} of this instance. + * @return the jar file type + */ + abstract JarFileType getType(); + + /** + * Return the security permission for this JAR. + * @return the security permission. + */ + abstract Permission getPermission(); + + /** + * Return an {@link InputStream} for the entire jar contents. + * @return the contents input stream + * @throws IOException on IO error + */ + abstract InputStream getInputStream() throws IOException; + + /** + * The type of a {@link JarFile}. + */ + enum JarFileType { + + /** + * DIRECT + */ + DIRECT, + + /** + * NESTED_DIRECTORY + */ + NESTED_DIRECTORY, + + /** + * NESTED_JAR + */ + NESTED_JAR + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java new file mode 100755 index 0000000..801719b --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/AsciiBytes.java @@ -0,0 +1,254 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.nio.charset.StandardCharsets; + +/** + * Simple wrapper around a byte array that represents an ASCII. Used for performance + * reasons to save constructing Strings for ZIP data. + * + * @author Phillip Webb + * @author Andy Wilkinson + */ +public class AsciiBytes { + + private static final String EMPTY_STRING = ""; + + private static final int[] INITIAL_BYTE_BITMASK = { 0x7F, 0x1F, 0x0F, 0x07 }; + + private static final int SUBSEQUENT_BYTE_BITMASK = 0x3F; + + private final byte[] bytes; + + private final int offset; + + private final int length; + + private String string; + + private int hash; + + /** + * Create a new {@link AsciiBytes} from the specified String. + * @param string the source string + */ + AsciiBytes(String string) { + this(string.getBytes(StandardCharsets.UTF_8)); + this.string = string; + } + + /** + * Create a new {@link AsciiBytes} from the specified bytes. NOTE: underlying bytes + * are not expected to change. + * @param bytes the source bytes + */ + AsciiBytes(byte[] bytes) { + this(bytes, 0, bytes.length); + } + + /** + * Create a new {@link AsciiBytes} from the specified bytes. NOTE: underlying bytes + * are not expected to change. + * @param bytes the source bytes + * @param offset the offset + * @param length the length + */ + AsciiBytes(byte[] bytes, int offset, int length) { + if (offset < 0 || length < 0 || (offset + length) > bytes.length) { + throw new IndexOutOfBoundsException(); + } + this.bytes = bytes; + this.offset = offset; + this.length = length; + } + + int length() { + return this.length; + } + + boolean startsWith(AsciiBytes prefix) { + if (this == prefix) { + return true; + } + if (prefix.length > this.length) { + return false; + } + for (int i = 0; i < prefix.length; i++) { + if (this.bytes[i + this.offset] != prefix.bytes[i + prefix.offset]) { + return false; + } + } + return true; + } + + boolean endsWith(AsciiBytes postfix) { + if (this == postfix) { + return true; + } + if (postfix.length > this.length) { + return false; + } + for (int i = 0; i < postfix.length; i++) { + if (this.bytes[this.offset + (this.length - 1) - i] != postfix.bytes[postfix.offset + (postfix.length - 1) + - i]) { + return false; + } + } + return true; + } + + AsciiBytes substring(int beginIndex) { + return substring(beginIndex, this.length); + } + + AsciiBytes substring(int beginIndex, int endIndex) { + int length = endIndex - beginIndex; + if (this.offset + length > this.bytes.length) { + throw new IndexOutOfBoundsException(); + } + return new AsciiBytes(this.bytes, this.offset + beginIndex, length); + } + + boolean matches(CharSequence name, char suffix) { + int charIndex = 0; + int nameLen = name.length(); + int totalLen = nameLen + ((suffix != 0) ? 1 : 0); + for (int i = this.offset; i < this.offset + this.length; i++) { + int b = this.bytes[i]; + int remainingUtfBytes = getNumberOfUtfBytes(b) - 1; + b &= INITIAL_BYTE_BITMASK[remainingUtfBytes]; + for (int j = 0; j < remainingUtfBytes; j++) { + b = (b << 6) + (this.bytes[++i] & SUBSEQUENT_BYTE_BITMASK); + } + char c = getChar(name, suffix, charIndex++); + if (b <= 0xFFFF) { + if (c != b) { + return false; + } + } else { + if (c != ((b >> 0xA) + 0xD7C0)) { + return false; + } + c = getChar(name, suffix, charIndex++); + if (c != ((b & 0x3FF) + 0xDC00)) { + return false; + } + } + } + return charIndex == totalLen; + } + + private char getChar(CharSequence name, char suffix, int index) { + if (index < name.length()) { + return name.charAt(index); + } + if (index == name.length()) { + return suffix; + } + return 0; + } + + private int getNumberOfUtfBytes(int b) { + if ((b & 0x80) == 0) { + return 1; + } + int numberOfUtfBytes = 0; + while ((b & 0x80) != 0) { + b <<= 1; + numberOfUtfBytes++; + } + return numberOfUtfBytes; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj.getClass() == AsciiBytes.class) { + AsciiBytes other = (AsciiBytes) obj; + if (this.length == other.length) { + for (int i = 0; i < this.length; i++) { + if (this.bytes[this.offset + i] != other.bytes[other.offset + i]) { + return false; + } + } + return true; + } + } + return false; + } + + @Override + public int hashCode() { + int hash = this.hash; + if (hash == 0 && this.bytes.length > 0) { + for (int i = this.offset; i < this.offset + this.length; i++) { + int b = this.bytes[i]; + int remainingUtfBytes = getNumberOfUtfBytes(b) - 1; + b &= INITIAL_BYTE_BITMASK[remainingUtfBytes]; + for (int j = 0; j < remainingUtfBytes; j++) { + b = (b << 6) + (this.bytes[++i] & SUBSEQUENT_BYTE_BITMASK); + } + if (b <= 0xFFFF) { + hash = 31 * hash + b; + } else { + hash = 31 * hash + ((b >> 0xA) + 0xD7C0); + hash = 31 * hash + ((b & 0x3FF) + 0xDC00); + } + } + this.hash = hash; + } + return hash; + } + + @Override + public String toString() { + if (this.string == null) { + if (this.length == 0) { + this.string = EMPTY_STRING; + } else { + this.string = new String(this.bytes, this.offset, this.length, StandardCharsets.UTF_8); + } + } + return this.string; + } + + static String toString(byte[] bytes) { + return new String(bytes, StandardCharsets.UTF_8); + } + + static int hashCode(CharSequence charSequence) { + // We're compatible with String's hashCode() + if (charSequence instanceof StringSequence) { + // ... but save making an unnecessary String for StringSequence + return charSequence.hashCode(); + } + return charSequence.toString().hashCode(); + } + + static int hashCode(int hash, char suffix) { + return (suffix != 0) ? (31 * hash + suffix) : hash; + } + +} + diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java new file mode 100755 index 0000000..5f3dc3c --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Bytes.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +/** + * Utilities for dealing with bytes from ZIP files. + * + * @author Phillip Webb + */ +public class Bytes { + + private Bytes() { + } + + static long littleEndianValue(byte[] bytes, int offset, int length) { + long value = 0; + for (int i = length - 1; i >= 0; i--) { + value = ((value << 8) | (bytes[offset + i] & 0xFF)); + } + return value; + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java new file mode 100755 index 0000000..49d86d3 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryEndRecord.java @@ -0,0 +1,250 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.IOException; + +/** + * A ZIP File "End of central directory record" (EOCD). + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Camille Vienot + * @see Zip File Format + */ +public class CentralDirectoryEndRecord { + + private static final int MINIMUM_SIZE = 22; + + private static final int MAXIMUM_COMMENT_LENGTH = 0xFFFF; + + private static final int ZIP64_MAGICCOUNT = 0xFFFF; + + private static final int MAXIMUM_SIZE = MINIMUM_SIZE + MAXIMUM_COMMENT_LENGTH; + + private static final int SIGNATURE = 0x06054b50; + + private static final int COMMENT_LENGTH_OFFSET = 20; + + private static final int READ_BLOCK_SIZE = 256; + + private final Zip64End zip64End; + + private byte[] block; + + private int offset; + + private int size; + + /** + * Create a new {@link CentralDirectoryEndRecord} instance from the specified + * {@link RandomAccessData}, searching backwards from the end until a valid block is + * located. + * @param data the source data + * @throws IOException in case of I/O errors + */ + CentralDirectoryEndRecord(RandomAccessData data) throws IOException { + this.block = createBlockFromEndOfData(data, READ_BLOCK_SIZE); + this.size = MINIMUM_SIZE; + this.offset = this.block.length - this.size; + while (!isValid()) { + this.size++; + if (this.size > this.block.length) { + if (this.size >= MAXIMUM_SIZE || this.size > data.getSize()) { + throw new IOException( + "Unable to find ZIP central directory records after reading " + this.size + " bytes"); + } + this.block = createBlockFromEndOfData(data, this.size + READ_BLOCK_SIZE); + } + this.offset = this.block.length - this.size; + } + int startOfCentralDirectoryEndRecord = (int) (data.getSize() - this.size); + this.zip64End = isZip64() ? new Zip64End(data, startOfCentralDirectoryEndRecord) : null; + } + + private byte[] createBlockFromEndOfData(RandomAccessData data, int size) throws IOException { + int length = (int) Math.min(data.getSize(), size); + return data.read(data.getSize() - length, length); + } + + private boolean isValid() { + if (this.block.length < MINIMUM_SIZE || + Bytes.littleEndianValue(this.block, this.offset + 0, 4) != SIGNATURE) { + return false; + } + // Total size must be the structure size + comment + long commentLength = Bytes.littleEndianValue(this.block, this.offset + COMMENT_LENGTH_OFFSET, 2); + return this.size == MINIMUM_SIZE + commentLength; + } + + private boolean isZip64() { + return (int) Bytes.littleEndianValue(this.block, this.offset + 10, 2) == ZIP64_MAGICCOUNT; + } + + /** + * Returns the location in the data that the archive actually starts. For most files + * the archive data will start at 0, however, it is possible to have prefixed bytes + * (often used for startup scripts) at the beginning of the data. + * @param data the source data + * @return the offset within the data where the archive begins + */ + long getStartOfArchive(RandomAccessData data) { + long length = Bytes.littleEndianValue(this.block, this.offset + 12, 4); + long specifiedOffset = Bytes.littleEndianValue(this.block, this.offset + 16, 4); + long zip64EndSize = (this.zip64End != null) ? this.zip64End.getSize() : 0L; + int zip64LocSize = (this.zip64End != null) ? Zip64Locator.ZIP64_LOCSIZE : 0; + long actualOffset = data.getSize() - this.size - length - zip64EndSize - zip64LocSize; + return actualOffset - specifiedOffset; + } + + /** + * Return the bytes of the "Central directory" based on the offset indicated in this + * record. + * @param data the source data + * @return the central directory data + */ + RandomAccessData getCentralDirectory(RandomAccessData data) { + if (this.zip64End != null) { + return this.zip64End.getCentralDirectory(data); + } + long offset = Bytes.littleEndianValue(this.block, this.offset + 16, 4); + long length = Bytes.littleEndianValue(this.block, this.offset + 12, 4); + return data.getSubsection(offset, length); + } + + /** + * Return the number of ZIP entries in the file. + * @return the number of records in the zip + */ + int getNumberOfRecords() { + if (this.zip64End != null) { + return this.zip64End.getNumberOfRecords(); + } + long numberOfRecords = Bytes.littleEndianValue(this.block, this.offset + 10, 2); + return (int) numberOfRecords; + } + + String getComment() { + int commentLength = (int) Bytes.littleEndianValue(this.block, this.offset + COMMENT_LENGTH_OFFSET, 2); + AsciiBytes comment = new AsciiBytes(this.block, this.offset + COMMENT_LENGTH_OFFSET + 2, commentLength); + return comment.toString(); + } + + /** + * A Zip64 end of central directory record. + * + * @see Chapter + * 4.3.14 of Zip64 specification + */ + private static final class Zip64End { + + private static final int ZIP64_ENDTOT = 32; // total number of entries + + private static final int ZIP64_ENDSIZ = 40; // central directory size in bytes + + private static final int ZIP64_ENDOFF = 48; // offset of first CEN header + + private final Zip64Locator locator; + + private final long centralDirectoryOffset; + + private final long centralDirectoryLength; + + private final int numberOfRecords; + + private Zip64End(RandomAccessData data, int centralDirectoryEndOffset) throws IOException { + this(data, new Zip64Locator(data, centralDirectoryEndOffset)); + } + + private Zip64End(RandomAccessData data, Zip64Locator locator) throws IOException { + this.locator = locator; + byte[] block = data.read(locator.getZip64EndOffset(), 56); + this.centralDirectoryOffset = Bytes.littleEndianValue(block, ZIP64_ENDOFF, 8); + this.centralDirectoryLength = Bytes.littleEndianValue(block, ZIP64_ENDSIZ, 8); + this.numberOfRecords = (int) Bytes.littleEndianValue(block, ZIP64_ENDTOT, 8); + } + + /** + * Return the size of this zip 64 end of central directory record. + * @return size of this zip 64 end of central directory record + */ + private long getSize() { + return this.locator.getZip64EndSize(); + } + + /** + * Return the bytes of the "Central directory" based on the offset indicated in + * this record. + * @param data the source data + * @return the central directory data + */ + private RandomAccessData getCentralDirectory(RandomAccessData data) { + return data.getSubsection(this.centralDirectoryOffset, this.centralDirectoryLength); + } + + /** + * Return the number of entries in the zip64 archive. + * @return the number of records in the zip + */ + private int getNumberOfRecords() { + return this.numberOfRecords; + } + + } + + /** + * A Zip64 end of central directory locator. + * + * @see Chapter + * 4.3.15 of Zip64 specification + */ + private static final class Zip64Locator { + + static final int ZIP64_LOCSIZE = 20; // locator size + static final int ZIP64_LOCOFF = 8; // offset of zip64 end + + private final long zip64EndOffset; + + private final int offset; + + private Zip64Locator(RandomAccessData data, int centralDirectoryEndOffset) throws IOException { + this.offset = centralDirectoryEndOffset - ZIP64_LOCSIZE; + byte[] block = data.read(this.offset, ZIP64_LOCSIZE); + this.zip64EndOffset = Bytes.littleEndianValue(block, ZIP64_LOCOFF, 8); + } + + /** + * Return the size of the zip 64 end record located by this zip64 end locator. + * @return size of the zip 64 end record located by this zip64 end locator + */ + private long getZip64EndSize() { + return this.offset - this.zip64EndOffset; + } + + /** + * Return the offset to locate {@link Zip64End}. + * @return offset of the Zip64 end of central directory record + */ + private long getZip64EndOffset() { + return this.zip64EndOffset; + } + + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java new file mode 100755 index 0000000..a2a4d12 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryFileHeader.java @@ -0,0 +1,191 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ValueRange; + +/** + * A ZIP File "Central directory file header record" (CDFH). + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Dmytro Nosan + * @see Zip File Format + */ +public class CentralDirectoryFileHeader implements FileHeader { + + private static final AsciiBytes SLASH = new AsciiBytes("/"); + + private static final byte[] NO_EXTRA = {}; + + private static final AsciiBytes NO_COMMENT = new AsciiBytes(""); + + private byte[] header; + + private int headerOffset; + + private AsciiBytes name; + + private byte[] extra; + + private AsciiBytes comment; + + private long localHeaderOffset; + + CentralDirectoryFileHeader() { + } + + CentralDirectoryFileHeader(byte[] header, int headerOffset, AsciiBytes name, byte[] extra, AsciiBytes comment, + long localHeaderOffset) { + this.header = header; + this.headerOffset = headerOffset; + this.name = name; + this.extra = extra; + this.comment = comment; + this.localHeaderOffset = localHeaderOffset; + } + + void load(byte[] data, int dataOffset, RandomAccessData variableData, int variableOffset, JarEntryFilter filter) + throws IOException { + // Load fixed part + this.header = data; + this.headerOffset = dataOffset; + long nameLength = Bytes.littleEndianValue(data, dataOffset + 28, 2); + long extraLength = Bytes.littleEndianValue(data, dataOffset + 30, 2); + long commentLength = Bytes.littleEndianValue(data, dataOffset + 32, 2); + this.localHeaderOffset = Bytes.littleEndianValue(data, dataOffset + 42, 4); + // Load variable part + dataOffset += 46; + if (variableData != null) { + data = variableData.read(variableOffset + 46, nameLength + extraLength + commentLength); + dataOffset = 0; + } + this.name = new AsciiBytes(data, dataOffset, (int) nameLength); + if (filter != null) { + this.name = filter.apply(this.name); + } + this.extra = NO_EXTRA; + this.comment = NO_COMMENT; + if (extraLength > 0) { + this.extra = new byte[(int) extraLength]; + System.arraycopy(data, (int) (dataOffset + nameLength), this.extra, 0, this.extra.length); + } + if (commentLength > 0) { + this.comment = new AsciiBytes(data, (int) (dataOffset + nameLength + extraLength), (int) commentLength); + } + } + + AsciiBytes getName() { + return this.name; + } + + @Override + public boolean hasName(CharSequence name, char suffix) { + return this.name.matches(name, suffix); + } + + boolean isDirectory() { + return this.name.endsWith(SLASH); + } + + @Override + public int getMethod() { + return (int) Bytes.littleEndianValue(this.header, this.headerOffset + 10, 2); + } + + long getTime() { + long datetime = Bytes.littleEndianValue(this.header, this.headerOffset + 12, 4); + return decodeMsDosFormatDateTime(datetime); + } + + /** + * Decode MS-DOS Date Time details. See + * Microsoft's documentation for more details of the format. + * @param datetime the date and time + * @return the date and time as milliseconds since the epoch + */ + private long decodeMsDosFormatDateTime(long datetime) { + int year = getChronoValue(((datetime >> 25) & 0x7f) + 1980, ChronoField.YEAR); + int month = getChronoValue((datetime >> 21) & 0x0f, ChronoField.MONTH_OF_YEAR); + int day = getChronoValue((datetime >> 16) & 0x1f, ChronoField.DAY_OF_MONTH); + int hour = getChronoValue((datetime >> 11) & 0x1f, ChronoField.HOUR_OF_DAY); + int minute = getChronoValue((datetime >> 5) & 0x3f, ChronoField.MINUTE_OF_HOUR); + int second = getChronoValue((datetime << 1) & 0x3e, ChronoField.SECOND_OF_MINUTE); + return ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.systemDefault()).toInstant() + .truncatedTo(ChronoUnit.SECONDS).toEpochMilli(); + } + + long getCrc() { + return Bytes.littleEndianValue(this.header, this.headerOffset + 16, 4); + } + + @Override + public long getCompressedSize() { + return Bytes.littleEndianValue(this.header, this.headerOffset + 20, 4); + } + + @Override + public long getSize() { + return Bytes.littleEndianValue(this.header, this.headerOffset + 24, 4); + } + + byte[] getExtra() { + return this.extra; + } + + boolean hasExtra() { + return this.extra.length > 0; + } + + AsciiBytes getComment() { + return this.comment; + } + + @Override + public long getLocalHeaderOffset() { + return this.localHeaderOffset; + } + + @Override + public CentralDirectoryFileHeader clone() { + byte[] header = new byte[46]; + System.arraycopy(this.header, this.headerOffset, header, 0, header.length); + return new CentralDirectoryFileHeader(header, 0, this.name, header, this.comment, this.localHeaderOffset); + } + + static CentralDirectoryFileHeader fromRandomAccessData(RandomAccessData data, int offset, JarEntryFilter filter) + throws IOException { + CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader(); + byte[] bytes = data.read(offset, 46); + fileHeader.load(bytes, 0, data, offset, filter); + return fileHeader; + } + + private static int getChronoValue(long value, ChronoField field) { + ValueRange range = field.range(); + return Math.toIntExact(Math.min(Math.max(value, range.getMinimum()), range.getMaximum())); + } + +} + diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java new file mode 100755 index 0000000..8548c4b --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryParser.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Parses the central directory from a JAR file. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @see CentralDirectoryVisitor + */ +public class CentralDirectoryParser { + + private static final int CENTRAL_DIRECTORY_HEADER_BASE_SIZE = 46; + + private final List visitors = new ArrayList<>(); + + T addVisitor(T visitor) { + this.visitors.add(visitor); + return visitor; + } + + /** + * Parse the source data, triggering {@link CentralDirectoryVisitor visitors}. + * @param data the source data + * @param skipPrefixBytes if prefix bytes should be skipped + * @return the actual archive data without any prefix bytes + * @throws IOException on error + */ + RandomAccessData parse(RandomAccessData data, boolean skipPrefixBytes) throws IOException { + CentralDirectoryEndRecord endRecord = new CentralDirectoryEndRecord(data); + if (skipPrefixBytes) { + data = getArchiveData(endRecord, data); + } + RandomAccessData centralDirectoryData = endRecord.getCentralDirectory(data); + visitStart(endRecord, centralDirectoryData); + parseEntries(endRecord, centralDirectoryData); + visitEnd(); + return data; + } + + private void parseEntries(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) + throws IOException { + byte[] bytes = centralDirectoryData.read(0, centralDirectoryData.getSize()); + CentralDirectoryFileHeader fileHeader = new CentralDirectoryFileHeader(); + int dataOffset = 0; + for (int i = 0; i < endRecord.getNumberOfRecords(); i++) { + fileHeader.load(bytes, dataOffset, null, 0, null); + visitFileHeader(dataOffset, fileHeader); + dataOffset += CENTRAL_DIRECTORY_HEADER_BASE_SIZE + fileHeader.getName().length() + + fileHeader.getComment().length() + fileHeader.getExtra().length; + } + } + + private RandomAccessData getArchiveData(CentralDirectoryEndRecord endRecord, RandomAccessData data) { + long offset = endRecord.getStartOfArchive(data); + if (offset == 0) { + return data; + } + return data.getSubsection(offset, data.getSize() - offset); + } + + private void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) { + for (CentralDirectoryVisitor visitor : this.visitors) { + visitor.visitStart(endRecord, centralDirectoryData); + } + } + + private void visitFileHeader(int dataOffset, CentralDirectoryFileHeader fileHeader) { + for (CentralDirectoryVisitor visitor : this.visitors) { + visitor.visitFileHeader(fileHeader, dataOffset); + } + } + + private void visitEnd() { + for (CentralDirectoryVisitor visitor : this.visitors) { + visitor.visitEnd(); + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java new file mode 100755 index 0000000..2f45f46 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/CentralDirectoryVisitor.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +/** + * Callback visitor triggered by {@link CentralDirectoryParser}. + * + * @author Phillip Webb + */ +public interface CentralDirectoryVisitor { + + /** + * visitStart + * @param endRecord endRecord + * @param centralDirectoryData centralDirectoryData + */ + void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData); + + /** + * visitFileHeader + * @param fileHeader fileHeader + * @param dataOffset dataOffset + */ + void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset); + + /** + * visitEnd + */ + void visitEnd(); + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java new file mode 100755 index 0000000..a360f64 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/FileHeader.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.util.zip.ZipEntry; + +/** + * A file header record that has been loaded from a Jar file. + * + * @author Phillip Webb + * @see JarEntry + * @see CentralDirectoryFileHeader + */ +public interface FileHeader { + + /** + * Returns {@code true} if the header has the given name. + * @param name the name to test + * @param suffix an additional suffix (or {@code 0}) + * @return {@code true} if the header has the given name + */ + boolean hasName(CharSequence name, char suffix); + + /** + * Return the offset of the load file header within the archive data. + * @return the local header offset + */ + long getLocalHeaderOffset(); + + /** + * Return the compressed size of the entry. + * @return the compressed size. + */ + long getCompressedSize(); + + /** + * Return the uncompressed size of the entry. + * @return the uncompressed size. + */ + long getSize(); + + /** + * Return the method used to compress the data. + * @return the zip compression method + * @see ZipEntry#STORED + * @see ZipEntry#DEFLATED + */ + int getMethod(); + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java new file mode 100755 index 0000000..517893d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/Handler.java @@ -0,0 +1,434 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import com.gitee.starblues.loader.PluginResourceStorage; + +import java.io.File; +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.net.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * {@link URLStreamHandler} for Spring Boot loader {@link JarFile}s. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @since 1.0.0 + * @see JarFile#registerUrlProtocolHandler() + */ +public class Handler extends URLStreamHandler { + + // NOTE: in order to be found as a URL protocol handler, this class must be public, + // must be named Handler and must be in a package ending '.jar' + + private static final String JAR_PROTOCOL = "jar:"; + + private static final String FILE_PROTOCOL = "file:"; + + private static final String TOMCAT_WARFILE_PROTOCOL = "war:file:"; + + private static final String SEPARATOR = "!/"; + + private static final Pattern SEPARATOR_PATTERN = Pattern.compile(SEPARATOR, Pattern.LITERAL); + + private static final String CURRENT_DIR = "/./"; + + private static final Pattern CURRENT_DIR_PATTERN = Pattern.compile(CURRENT_DIR, Pattern.LITERAL); + + private static final String PARENT_DIR = "/../"; + + private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs"; + + private static final String[] FALLBACK_HANDLERS = { "sun.net.www.protocol.jar.Handler" }; + + private static URL jarContextUrl; + + private final JarFile jarFile; + + private URLStreamHandler fallbackHandler; + + public Handler() { + this(null); + } + + public Handler(JarFile jarFile) { + this.jarFile = jarFile; + } + + @Override + protected URLConnection openConnection(URL url) throws IOException { + URLConnection jarURLConnection = null; + if (this.jarFile != null && isUrlInJarFile(url, this.jarFile)) { + jarURLConnection = JarURLConnection.get(url, this.jarFile); + } else { + try { + jarURLConnection = JarURLConnection.get(url, getRootJarFileFromUrl(url)); + } catch (Exception ex) { + jarURLConnection = openFallbackConnection(url, ex); + } + } + return jarURLConnection; + } + + private boolean isUrlInJarFile(URL url, JarFile jarFile) throws MalformedURLException { + // Try the path first to save building a new url string each time + return url.getPath().startsWith(jarFile.getUrl().getPath()) + && url.toString().startsWith(jarFile.getUrlString()); + } + + private URLConnection openFallbackConnection(URL url, Exception reason) throws IOException { + try { + URLConnection connection = openFallbackTomcatConnection(url); + connection = (connection != null) ? connection : openFallbackContextConnection(url); + return (connection != null) ? connection : openFallbackHandlerConnection(url); + } catch (Exception ex) { + if (reason instanceof IOException) { + log(false, "Unable to open fallback handler", ex); + throw (IOException) reason; + } + log(true, "Unable to open fallback handler", ex); + if (reason instanceof RuntimeException) { + throw (RuntimeException) reason; + } + throw new IllegalStateException(reason); + } + } + + /** + * Attempt to open a Tomcat formatted 'jar:war:file:...' URL. This method allows us to + * use our own nested JAR support to open the content rather than the logic in + * {@code sun.net.www.protocol.jar.URLJarFile} which will extract the nested jar to + * the temp folder to that its content can be accessed. + * @param url the URL to open + * @return a {@link URLConnection} or {@code null} + */ + private URLConnection openFallbackTomcatConnection(URL url) { + String file = url.getFile(); + if (isTomcatWarUrl(file)) { + file = file.substring(TOMCAT_WARFILE_PROTOCOL.length()); + file = file.replaceFirst("\\*/", "!/"); + try { + URLConnection connection = openConnection(new URL("jar:file:" + file)); + connection.getInputStream().close(); + return connection; + } catch (IOException ex) { + // ignore + } + } + return null; + } + + private boolean isTomcatWarUrl(String file) { + if (file.startsWith(TOMCAT_WARFILE_PROTOCOL) || !file.contains("*/")) { + try { + URLConnection connection = new URL(file).openConnection(); + if (connection.getClass().getName().startsWith("org.apache.catalina")) { + return true; + } + } catch (Exception ex) { + // ignore + } + } + return false; + } + + /** + * Attempt to open a fallback connection by using a context URL captured before the + * jar handler was replaced with our own version. Since this method doesn't use + * reflection it won't trigger "illegal reflective access operation has occurred" + * warnings on Java 13+. + * @param url the URL to open + * @return a {@link URLConnection} or {@code null} + */ + private URLConnection openFallbackContextConnection(URL url) { + try { + if (jarContextUrl != null) { + return new URL(jarContextUrl, url.toExternalForm()).openConnection(); + } + } catch (Exception ex) { + // ignore + } + return null; + } + + /** + * Attempt to open a fallback connection by using reflection to access Java's default + * jar {@link URLStreamHandler}. + * @param url the URL to open + * @return the {@link URLConnection} + * @throws Exception if not connection could be opened + */ + private URLConnection openFallbackHandlerConnection(URL url) throws Exception { + URLStreamHandler fallbackHandler = getFallbackHandler(); + return new URL(null, url.toExternalForm(), fallbackHandler).openConnection(); + } + + private URLStreamHandler getFallbackHandler() { + if (this.fallbackHandler != null) { + return this.fallbackHandler; + } + for (String handlerClassName : FALLBACK_HANDLERS) { + try { + Class handlerClass = Class.forName(handlerClassName); + this.fallbackHandler = (URLStreamHandler) handlerClass.getDeclaredConstructor().newInstance(); + return this.fallbackHandler; + } catch (Exception ex) { + // Ignore + } + } + throw new IllegalStateException("Unable to find fallback handler"); + } + + private void log(boolean warning, String message, Exception cause) { + try { + Level level = warning ? Level.WARNING : Level.FINEST; + Logger.getLogger(getClass().getName()).log(level, message, cause); + } catch (Exception ex) { + if (warning) { + System.err.println("WARNING: " + message); + } + } + } + + @Override + protected void parseURL(URL context, String spec, int start, int limit) { + if (spec.regionMatches(true, 0, JAR_PROTOCOL, 0, JAR_PROTOCOL.length())) { + setFile(context, getFileFromSpec(spec.substring(start, limit))); + } else { + setFile(context, getFileFromContext(context, spec.substring(start, limit))); + } + } + + private String getFileFromSpec(String spec) { + int separatorIndex = spec.lastIndexOf("!/"); + if (separatorIndex == -1) { + throw new IllegalArgumentException("No !/ in spec '" + spec + "'"); + } + try { + new URL(spec.substring(0, separatorIndex)); + return spec; + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("Invalid spec URL '" + spec + "'", ex); + } + } + + private String getFileFromContext(URL context, String spec) { + String file = context.getFile(); + if (spec.startsWith("/")) { + return trimToJarRoot(file) + SEPARATOR + spec.substring(1); + } + if (file.endsWith("/")) { + return file + spec; + } + int lastSlashIndex = file.lastIndexOf('/'); + if (lastSlashIndex == -1) { + throw new IllegalArgumentException("No / found in context URL's file '" + file + "'"); + } + return file.substring(0, lastSlashIndex + 1) + spec; + } + + private String trimToJarRoot(String file) { + int lastSeparatorIndex = file.lastIndexOf(SEPARATOR); + if (lastSeparatorIndex == -1) { + throw new IllegalArgumentException("No !/ found in context URL's file '" + file + "'"); + } + return file.substring(0, lastSeparatorIndex); + } + + private void setFile(URL context, String file) { + String path = normalize(file); + String query = null; + int queryIndex = path.lastIndexOf('?'); + if (queryIndex != -1) { + query = path.substring(queryIndex + 1); + path = path.substring(0, queryIndex); + } + setURL(context, JAR_PROTOCOL, null, -1, null, null, path, query, context.getRef()); + } + + private String normalize(String file) { + if (!file.contains(CURRENT_DIR) && !file.contains(PARENT_DIR)) { + return file; + } + int afterLastSeparatorIndex = file.lastIndexOf(SEPARATOR) + SEPARATOR.length(); + String afterSeparator = file.substring(afterLastSeparatorIndex); + afterSeparator = replaceParentDir(afterSeparator); + afterSeparator = replaceCurrentDir(afterSeparator); + return file.substring(0, afterLastSeparatorIndex) + afterSeparator; + } + + private String replaceParentDir(String file) { + int parentDirIndex; + while ((parentDirIndex = file.indexOf(PARENT_DIR)) >= 0) { + int precedingSlashIndex = file.lastIndexOf('/', parentDirIndex - 1); + if (precedingSlashIndex >= 0) { + file = file.substring(0, precedingSlashIndex) + file.substring(parentDirIndex + 3); + } else { + file = file.substring(parentDirIndex + 4); + } + } + return file; + } + + private String replaceCurrentDir(String file) { + return CURRENT_DIR_PATTERN.matcher(file).replaceAll("/"); + } + + @Override + protected int hashCode(URL u) { + return hashCode(u.getProtocol(), u.getFile()); + } + + private int hashCode(String protocol, String file) { + int result = (protocol != null) ? protocol.hashCode() : 0; + int separatorIndex = file.indexOf(SEPARATOR); + if (separatorIndex == -1) { + return result + file.hashCode(); + } + String source = file.substring(0, separatorIndex); + String entry = canonicalize(file.substring(separatorIndex + 2)); + try { + result += new URL(source).hashCode(); + } catch (MalformedURLException ex) { + result += source.hashCode(); + } + result += entry.hashCode(); + return result; + } + + @Override + protected boolean sameFile(URL u1, URL u2) { + if (!"jar".equals(u1.getProtocol()) || !"jar".equals(u2.getProtocol())) { + return false; + } + int separator1 = u1.getFile().indexOf(SEPARATOR); + int separator2 = u2.getFile().indexOf(SEPARATOR); + if (separator1 == -1 || separator2 == -1) { + return super.sameFile(u1, u2); + } + String nested1 = u1.getFile().substring(separator1 + SEPARATOR.length()); + String nested2 = u2.getFile().substring(separator2 + SEPARATOR.length()); + if (!nested1.equals(nested2)) { + String canonical1 = canonicalize(nested1); + String canonical2 = canonicalize(nested2); + if (!canonical1.equals(canonical2)) { + return false; + } + } + String root1 = u1.getFile().substring(0, separator1); + String root2 = u2.getFile().substring(0, separator2); + try { + return super.sameFile(new URL(root1), new URL(root2)); + } catch (MalformedURLException ex) { + // Continue + } + return super.sameFile(u1, u2); + } + + private String canonicalize(String path) { + return SEPARATOR_PATTERN.matcher(path).replaceAll("/"); + } + + public JarFile getRootJarFileFromUrl(URL url) throws IOException { + String spec = url.getFile(); + int separatorIndex = spec.indexOf(SEPARATOR); + if (separatorIndex == -1) { + throw new MalformedURLException("Jar URL does not contain !/ separator"); + } + String name = spec.substring(0, separatorIndex); + return getRootJarFile(name); + } + + private JarFile getRootJarFile(String name) throws IOException { + try { + if (!name.startsWith(FILE_PROTOCOL)) { + throw new IllegalStateException("Not a file URL"); + } + File file = new File(URI.create(name)); + JarFileWrapper jarFile = PluginResourceStorage.getRootJarFile(file); + if (jarFile == null) { + jarFile = new JarFileWrapper(new JarFile(file)); + PluginResourceStorage.addRootJarFile(file, jarFile); + } + return jarFile; + } catch (Exception ex) { + throw new IOException("Unable to open root Jar file '" + name + "'", ex); + } + } + + /** + * If possible, capture a URL that is configured with the original jar handler so that + * we can use it as a fallback context later. We can only do this if we know that we + * can reset the handlers after. + */ + static void captureJarContextUrl() { + if (canResetCachedUrlHandlers()) { + String handlers = System.getProperty(PROTOCOL_HANDLER, ""); + try { + System.clearProperty(PROTOCOL_HANDLER); + try { + resetCachedUrlHandlers(); + jarContextUrl = new URL("jar:file:context.jar!/"); + URLConnection connection = jarContextUrl.openConnection(); + if (connection instanceof JarURLConnection) { + jarContextUrl = null; + } + } catch (Exception ex) { + // ignore + } + } finally { + if (handlers == null) { + System.clearProperty(PROTOCOL_HANDLER); + } else { + System.setProperty(PROTOCOL_HANDLER, handlers); + } + } + resetCachedUrlHandlers(); + } + } + + private static boolean canResetCachedUrlHandlers() { + try { + resetCachedUrlHandlers(); + return true; + } catch (Error ex) { + return false; + } + } + + private static void resetCachedUrlHandlers() { + URL.setURLStreamHandlerFactory(null); + } + + /** + * Set if a generic static exception can be thrown when a URL cannot be connected. + * This optimization is used during class loading to save creating lots of exceptions + * which are then swallowed. + * @param useFastConnectionExceptions if fast connection exceptions can be used. + */ + public static void setUseFastConnectionExceptions(boolean useFastConnectionExceptions) { + JarURLConnection.setUseFastExceptions(useFastConnectionExceptions); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java new file mode 100755 index 0000000..165e79d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntry.java @@ -0,0 +1,122 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.CodeSigner; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Extended variant of {@link java.util.jar.JarEntry} returned by {@link JarFile}s. + * + * @author Phillip Webb + * @author Andy Wilkinson + */ +public class JarEntry extends java.util.jar.JarEntry implements FileHeader { + + private final int index; + + private final AsciiBytes name; + + private final AsciiBytes headerName; + + private final JarFile jarFile; + + private long localHeaderOffset; + + private volatile JarEntryCertification certification; + + JarEntry(JarFile jarFile, int index, CentralDirectoryFileHeader header, AsciiBytes nameAlias) { + super((nameAlias != null) ? nameAlias.toString() : header.getName().toString()); + this.index = index; + this.name = (nameAlias != null) ? nameAlias : header.getName(); + this.headerName = header.getName(); + this.jarFile = jarFile; + this.localHeaderOffset = header.getLocalHeaderOffset(); + setCompressedSize(header.getCompressedSize()); + setMethod(header.getMethod()); + setCrc(header.getCrc()); + setComment(header.getComment().toString()); + setSize(header.getSize()); + setTime(header.getTime()); + if (header.hasExtra()) { + setExtra(header.getExtra()); + } + } + + int getIndex() { + return this.index; + } + + AsciiBytes getAsciiBytesName() { + return this.name; + } + + @Override + public boolean hasName(CharSequence name, char suffix) { + return this.headerName.matches(name, suffix); + } + + /** + * Return a {@link URL} for this {@link JarEntry}. + * @return the URL for the entry + * @throws MalformedURLException if the URL is not valid + */ + URL getUrl() throws MalformedURLException { + return new URL(this.jarFile.getUrl(), getName()); + } + + @Override + public Attributes getAttributes() throws IOException { + Manifest manifest = this.jarFile.getManifest(); + return (manifest != null) ? manifest.getAttributes(getName()) : null; + } + + @Override + public Certificate[] getCertificates() { + return getCertification().getCertificates(); + } + + @Override + public CodeSigner[] getCodeSigners() { + return getCertification().getCodeSigners(); + } + + private JarEntryCertification getCertification() { + if (!this.jarFile.isSigned()) { + return JarEntryCertification.NONE; + } + JarEntryCertification certification = this.certification; + if (certification == null) { + certification = this.jarFile.getCertification(this); + this.certification = certification; + } + return certification; + } + + @Override + public long getLocalHeaderOffset() { + return this.localHeaderOffset; + } + +} + diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java new file mode 100755 index 0000000..2ba0a16 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryCertification.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.security.CodeSigner; +import java.security.cert.Certificate; + +/** + * {@link Certificate} and {@link CodeSigner} details for a {@link JarEntry} from a signed + * {@link JarFile}. + * + * @author Phillip Webb + */ +public class JarEntryCertification { + + static final JarEntryCertification NONE = new JarEntryCertification(null, null); + + private final Certificate[] certificates; + + private final CodeSigner[] codeSigners; + + JarEntryCertification(Certificate[] certificates, CodeSigner[] codeSigners) { + this.certificates = certificates; + this.codeSigners = codeSigners; + } + + Certificate[] getCertificates() { + return (this.certificates != null) ? this.certificates.clone() : null; + } + + CodeSigner[] getCodeSigners() { + return (this.codeSigners != null) ? this.codeSigners.clone() : null; + } + + static JarEntryCertification from(java.util.jar.JarEntry certifiedEntry) { + Certificate[] certificates = (certifiedEntry != null) ? certifiedEntry.getCertificates() : null; + CodeSigner[] codeSigners = (certifiedEntry != null) ? certifiedEntry.getCodeSigners() : null; + if (certificates == null && codeSigners == null) { + return NONE; + } + return new JarEntryCertification(certificates, codeSigners); + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java new file mode 100755 index 0000000..e6990e7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarEntryFilter.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +/** + * Interface that can be used to filter and optionally rename jar entries. + * + * @author Phillip Webb + */ +public interface JarEntryFilter { + + /** + * Apply the jar entry filter. + * @param name the current entry name. This may be different that the original entry + * name if a previous filter has been applied + * @return the new name of the entry or {@code null} if the entry should not be + * included. + */ + AsciiBytes apply(AsciiBytes name); + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java new file mode 100755 index 0000000..a02274b --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFile.java @@ -0,0 +1,453 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.security.Permission; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Supplier; +import java.util.jar.Manifest; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import java.util.zip.ZipEntry; + +/** + * Extended variant of {@link java.util.jar.JarFile} that behaves in the same way but + * offers the following additional functionality. + *

    + *
  • A nested {@link JarFile} can be {@link #getNestedJarFile(ZipEntry) obtained} based + * on any directory entry.
  • + *
  • A nested {@link JarFile} can be {@link #getNestedJarFile(ZipEntry) obtained} for + * embedded JAR files (as long as their entry is not compressed).
  • + *
+ * + * @author Phillip Webb + * @author Andy Wilkinson + * @since 1.0.0 + */ +public class JarFile extends AbstractJarFile implements Iterable { + + + private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + private static final String PROTOCOL_HANDLER = "java.protocol.handler.pkgs"; + + private static final String HANDLERS_PACKAGE = "com.gitee.starblues.loader"; + + private static final AsciiBytes META_INF = new AsciiBytes("META-INF/"); + + private static final AsciiBytes SIGNATURE_FILE_EXTENSION = new AsciiBytes(".SF"); + + private static final String READ_ACTION = "read"; + + private final RandomAccessDataFile rootFile; + + private final String pathFromRoot; + + private final RandomAccessData data; + + private final JarFileType type; + + private URL url; + + private String urlString; + + private JarFileEntries entries; + + private Supplier manifestSupplier; + + private SoftReference manifest; + + private boolean signed; + + private String comment; + + private volatile boolean closed; + + /** + * Create a new {@link JarFile} backed by the specified file. + * @param file the root jar file + * @throws IOException if the file cannot be read + */ + public JarFile(File file) throws IOException { + this(new RandomAccessDataFile(file)); + } + + /** + * Create a new {@link JarFile} backed by the specified file. + * @param file the root jar file + * @throws IOException if the file cannot be read + */ + JarFile(RandomAccessDataFile file) throws IOException { + this(file, "", file, JarFileType.DIRECT); + } + + /** + * Private constructor used to create a new {@link JarFile} either directly or from a + * nested entry. + * @param rootFile the root jar file + * @param pathFromRoot the name of this file + * @param data the underlying data + * @param type the type of the jar file + * @throws IOException if the file cannot be read + */ + private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarFileType type) + throws IOException { + this(rootFile, pathFromRoot, data, null, type, null); + } + + private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarEntryFilter filter, + JarFileType type, Supplier manifestSupplier) throws IOException { + super(rootFile.getFile()); + super.close(); + this.rootFile = rootFile; + this.pathFromRoot = pathFromRoot; + CentralDirectoryParser parser = new CentralDirectoryParser(); + this.entries = parser.addVisitor(new JarFileEntries(this, filter)); + this.type = type; + parser.addVisitor(centralDirectoryVisitor()); + try { + this.data = parser.parse(data, filter == null); + } catch (RuntimeException ex) { + close(); + throw ex; + } + this.manifestSupplier = (manifestSupplier != null) ? manifestSupplier : () -> { + try (InputStream inputStream = getInputStream(MANIFEST_NAME)) { + if (inputStream == null) { + return null; + } + return new Manifest(inputStream); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }; + } + + private CentralDirectoryVisitor centralDirectoryVisitor() { + return new CentralDirectoryVisitor() { + + @Override + public void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) { + JarFile.this.comment = endRecord.getComment(); + } + + @Override + public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) { + AsciiBytes name = fileHeader.getName(); + if (name.startsWith(META_INF) && name.endsWith(SIGNATURE_FILE_EXTENSION)) { + JarFile.this.signed = true; + } + } + + @Override + public void visitEnd() { + } + + }; + } + + @Override + Permission getPermission() { + return new FilePermission(this.rootFile.getFile().getPath(), READ_ACTION); + } + + protected final RandomAccessDataFile getRootJarFile() { + return this.rootFile; + } + + RandomAccessData getData() { + return this.data; + } + + @Override + public Manifest getManifest() throws IOException { + Manifest manifest = (this.manifest != null) ? this.manifest.get() : null; + if (manifest == null) { + try { + manifest = this.manifestSupplier.get(); + } catch (RuntimeException ex) { + throw new IOException(ex); + } + this.manifest = new SoftReference<>(manifest); + } + return manifest; + } + + @Override + public Enumeration entries() { + return new JarEntryEnumeration(this.entries.iterator()); + } + + @Override + public Stream stream() { + Spliterator spliterator = Spliterators.spliterator(iterator(), size(), + Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL); + return StreamSupport.stream(spliterator, false); + } + + /** + * Return an iterator for the contained entries. + * @see java.lang.Iterable#iterator() + * @since 2.3.0 + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Iterator iterator() { + return (Iterator) this.entries.iterator(this::ensureOpen); + } + + public JarEntry getJarEntry(CharSequence name) { + return this.entries.getEntry(name); + } + + @Override + public JarEntry getJarEntry(String name) { + return (JarEntry) getEntry(name); + } + + public boolean containsEntry(String name) { + return this.entries.containsEntry(name); + } + + @Override + public ZipEntry getEntry(String name) { + ensureOpen(); + return this.entries.getEntry(name); + } + + @Override + InputStream getInputStream() throws IOException { + return this.data.getInputStream(); + } + + @Override + public synchronized InputStream getInputStream(ZipEntry entry) throws IOException { + ensureOpen(); + if (entry instanceof JarEntry) { + return this.entries.getInputStream((JarEntry) entry); + } + return getInputStream((entry != null) ? entry.getName() : null); + } + + InputStream getInputStream(String name) throws IOException { + return this.entries.getInputStream(name); + } + + /** + * Return a nested {@link JarFile} loaded from the specified entry. + * @param entry the zip entry + * @return a {@link JarFile} for the entry + * @throws IOException if the nested jar file cannot be read + */ + public synchronized JarFile getNestedJarFile(ZipEntry entry) throws IOException { + return getNestedJarFile((JarEntry) entry); + } + + /** + * Return a nested {@link JarFile} loaded from the specified entry. + * @param entry the zip entry + * @return a {@link JarFile} for the entry + * @throws IOException if the nested jar file cannot be read + */ + public synchronized JarFile getNestedJarFile(JarEntry entry) throws IOException { + try { + return createJarFileFromEntry(entry); + } catch (Exception ex) { + throw new IOException("Unable to open nested jar file '" + entry.getName() + "'", ex); + } + } + + private JarFile createJarFileFromEntry(JarEntry entry) throws IOException { + if (entry.isDirectory()) { + return createJarFileFromDirectoryEntry(entry); + } + return createJarFileFromFileEntry(entry); + } + + private JarFile createJarFileFromDirectoryEntry(JarEntry entry) throws IOException { + AsciiBytes name = entry.getAsciiBytesName(); + JarEntryFilter filter = (candidate) -> { + if (candidate.startsWith(name) && !candidate.equals(name)) { + return candidate.substring(name.length()); + } + return null; + }; + return new JarFile(this.rootFile, this.pathFromRoot + "!/" + entry.getName().substring(0, name.length() - 1), + this.data, filter, JarFileType.NESTED_DIRECTORY, this.manifestSupplier); + } + + private JarFile createJarFileFromFileEntry(JarEntry entry) throws IOException { + if (entry.getMethod() != ZipEntry.STORED) { + throw new IllegalStateException( + "Unable to open nested entry '" + entry.getName() + "'. It has been compressed and nested " + + "jar files must be stored without compression. Please check the " + + "mechanism used to create your executable jar file"); + } + RandomAccessData entryData = this.entries.getEntryData(entry.getName()); + return new JarFile(this.rootFile, this.pathFromRoot + "!/" + entry.getName(), entryData, + JarFileType.NESTED_JAR); + } + + @Override + public String getComment() { + ensureOpen(); + return this.comment; + } + + @Override + public int size() { + ensureOpen(); + return this.entries.getSize(); + } + + @Override + public void close() throws IOException { + if (this.closed) { + return; + } + this.closed = true; + if (this.type == JarFileType.DIRECT) { + this.rootFile.close(); + } + entries.clearCache(); + } + + private void ensureOpen() { + if (this.closed) { + throw new IllegalStateException("zip file closed"); + } + } + + boolean isClosed() { + return this.closed; + } + + String getUrlString() throws MalformedURLException { + if (this.urlString == null) { + this.urlString = getUrl().toString(); + } + return this.urlString; + } + + @Override + public URL getUrl() throws MalformedURLException { + if (this.url == null) { + String file = this.rootFile.getFile().toURI() + this.pathFromRoot + "!/"; + file = file.replace("file:////", "file://"); // Fix UNC paths + this.url = new URL("jar", "", -1, file, new Handler(this)); + } + return this.url; + } + + @Override + public String toString() { + return getName(); + } + + @Override + public String getName() { + return this.rootFile.getFile() + this.pathFromRoot; + } + + boolean isSigned() { + return this.signed; + } + + JarEntryCertification getCertification(JarEntry entry) { + try { + return this.entries.getCertification(entry); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + public void clearCache() { + this.entries.clearCache(); + } + + protected String getPathFromRoot() { + return this.pathFromRoot; + } + + @Override + JarFileType getType() { + return this.type; + } + + /** + * Register a {@literal 'java.protocol.handler.pkgs'} property so that a + * {@link URLStreamHandler} will be located to deal with jar URLs. + */ + public static void registerUrlProtocolHandler() { + Handler.captureJarContextUrl(); + String handlers = System.getProperty(PROTOCOL_HANDLER, ""); + System.setProperty(PROTOCOL_HANDLER, + ((handlers == null || handlers.isEmpty()) ? HANDLERS_PACKAGE : handlers + "|" + HANDLERS_PACKAGE)); + resetCachedUrlHandlers(); + } + + /** + * Reset any cached handlers just in case a jar protocol has already been used. We + * reset the handler by trying to set a null {@link URLStreamHandlerFactory} which + * should have no effect other than clearing the handlers cache. + */ + private static void resetCachedUrlHandlers() { + try { + URL.setURLStreamHandlerFactory(null); + } catch (Error ex) { + // Ignore + } + } + + /** + * An {@link Enumeration} on {@linkplain java.util.jar.JarEntry jar entries}. + */ + private static class JarEntryEnumeration implements Enumeration { + + private final Iterator iterator; + + JarEntryEnumeration(Iterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasMoreElements() { + return this.iterator.hasNext(); + } + + @Override + public java.util.jar.JarEntry nextElement() { + return this.iterator.next(); + } + + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java new file mode 100755 index 0000000..ce3cf70 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileEntries.java @@ -0,0 +1,412 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +/** + * Provides access to entries from a {@link JarFile}. In order to reduce memory + * consumption entry details are stored using arrays. The {@code hashCodes} array stores + * the hash code of the entry name, the {@code centralDirectoryOffsets} provides the + * offset to the central directory record and {@code positions} provides the original + * order position of the entry. The arrays are stored in hashCode order so that a binary + * search can be used to find a name. + *

+ * A typical Spring Boot application will have somewhere in the region of 10,500 entries + * which should consume about 122K. + * + * @author Phillip Webb + * @author Andy Wilkinson + */ +public class JarFileEntries implements CentralDirectoryVisitor, Iterable { + + private static final Runnable NO_VALIDATION = () -> { + }; + + private static final String META_INF_PREFIX = "META-INF/"; + + private static final Attributes.Name MULTI_RELEASE = new Attributes.Name("Multi-Release"); + + private static final int BASE_VERSION = 8; + + private static final int RUNTIME_VERSION; + + static { + int version; + try { + Object runtimeVersion = Runtime.class.getMethod("version").invoke(null); + version = (int) runtimeVersion.getClass().getMethod("major").invoke(runtimeVersion); + } catch (Throwable ex) { + version = BASE_VERSION; + } + RUNTIME_VERSION = version; + } + + private static final long LOCAL_FILE_HEADER_SIZE = 30; + + private static final char SLASH = '/'; + + private static final char NO_SUFFIX = 0; + + protected static final int ENTRY_CACHE_SIZE = 25; + + private final JarFile jarFile; + + private final JarEntryFilter filter; + + private RandomAccessData centralDirectoryData; + + private int size; + + private int[] hashCodes; + + private int[] centralDirectoryOffsets; + + private int[] positions; + + private Boolean multiReleaseJar; + + private JarEntryCertification[] certifications; + + private final Map entriesCache = Collections + .synchronizedMap(new LinkedHashMap(16, 0.75f, true) { + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() >= ENTRY_CACHE_SIZE; + } + + }); + + JarFileEntries(JarFile jarFile, JarEntryFilter filter) { + this.jarFile = jarFile; + this.filter = filter; + if (RUNTIME_VERSION == BASE_VERSION) { + this.multiReleaseJar = false; + } + } + + @Override + public void visitStart(CentralDirectoryEndRecord endRecord, RandomAccessData centralDirectoryData) { + int maxSize = endRecord.getNumberOfRecords(); + this.centralDirectoryData = centralDirectoryData; + this.hashCodes = new int[maxSize]; + this.centralDirectoryOffsets = new int[maxSize]; + this.positions = new int[maxSize]; + } + + @Override + public void visitFileHeader(CentralDirectoryFileHeader fileHeader, int dataOffset) { + AsciiBytes name = applyFilter(fileHeader.getName()); + if (name != null) { + add(name, dataOffset); + } + } + + private void add(AsciiBytes name, int dataOffset) { + this.hashCodes[this.size] = name.hashCode(); + this.centralDirectoryOffsets[this.size] = dataOffset; + this.positions[this.size] = this.size; + this.size++; + } + + @Override + public void visitEnd() { + sort(0, this.size - 1); + int[] positions = this.positions; + this.positions = new int[positions.length]; + for (int i = 0; i < this.size; i++) { + this.positions[positions[i]] = i; + } + } + + int getSize() { + return this.size; + } + + private void sort(int left, int right) { + // Quick sort algorithm, uses hashCodes as the source but sorts all arrays + if (left < right) { + int pivot = this.hashCodes[left + (right - left) / 2]; + int i = left; + int j = right; + while (i <= j) { + while (this.hashCodes[i] < pivot) { + i++; + } + while (this.hashCodes[j] > pivot) { + j--; + } + if (i <= j) { + swap(i, j); + i++; + j--; + } + } + if (left < j) { + sort(left, j); + } + if (right > i) { + sort(i, right); + } + } + } + + private void swap(int i, int j) { + swap(this.hashCodes, i, j); + swap(this.centralDirectoryOffsets, i, j); + swap(this.positions, i, j); + } + + private void swap(int[] array, int i, int j) { + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + @Override + public Iterator iterator() { + return new EntryIterator(NO_VALIDATION); + } + + Iterator iterator(Runnable validator) { + return new EntryIterator(validator); + } + + boolean containsEntry(CharSequence name) { + return getEntry(name, FileHeader.class, true) != null; + } + + JarEntry getEntry(CharSequence name) { + return getEntry(name, JarEntry.class, true); + } + + InputStream getInputStream(String name) throws IOException { + FileHeader entry = getEntry(name, FileHeader.class, false); + return getInputStream(entry); + } + + InputStream getInputStream(FileHeader entry) throws IOException { + if (entry == null) { + return null; + } + InputStream inputStream = getEntryData(entry).getInputStream(); + if (entry.getMethod() == ZipEntry.DEFLATED) { + inputStream = new ZipInflaterInputStream(inputStream, (int) entry.getSize()); + } + return inputStream; + } + + RandomAccessData getEntryData(String name) throws IOException { + FileHeader entry = getEntry(name, FileHeader.class, false); + if (entry == null) { + return null; + } + return getEntryData(entry); + } + + private RandomAccessData getEntryData(FileHeader entry) throws IOException { + // aspectjrt-1.7.4.jar has a different ext bytes length in the + // local directory to the central directory. We need to re-read + // here to skip them + RandomAccessData data = this.jarFile.getData(); + byte[] localHeader = data.read(entry.getLocalHeaderOffset(), LOCAL_FILE_HEADER_SIZE); + long nameLength = Bytes.littleEndianValue(localHeader, 26, 2); + long extraLength = Bytes.littleEndianValue(localHeader, 28, 2); + return data.getSubsection(entry.getLocalHeaderOffset() + LOCAL_FILE_HEADER_SIZE + nameLength + extraLength, + entry.getCompressedSize()); + } + + private T getEntry(CharSequence name, Class type, boolean cacheEntry) { + T entry = doGetEntry(name, type, cacheEntry, null); + if (!isMetaInfEntry(name) && isMultiReleaseJar()) { + int version = RUNTIME_VERSION; + AsciiBytes nameAlias = (entry instanceof JarEntry) ? ((JarEntry) entry).getAsciiBytesName() + : new AsciiBytes(name.toString()); + while (version > BASE_VERSION) { + T versionedEntry = doGetEntry("META-INF/versions/" + version + "/" + name, type, cacheEntry, nameAlias); + if (versionedEntry != null) { + return versionedEntry; + } + version--; + } + } + return entry; + } + + private boolean isMetaInfEntry(CharSequence name) { + return name.toString().startsWith(META_INF_PREFIX); + } + + private boolean isMultiReleaseJar() { + Boolean multiRelease = this.multiReleaseJar; + if (multiRelease != null) { + return multiRelease; + } + try { + Manifest manifest = this.jarFile.getManifest(); + if (manifest == null) { + multiRelease = false; + } else { + Attributes attributes = manifest.getMainAttributes(); + multiRelease = attributes.containsKey(MULTI_RELEASE); + } + } catch (IOException ex) { + multiRelease = false; + } + this.multiReleaseJar = multiRelease; + return multiRelease; + } + + private T doGetEntry(CharSequence name, Class type, boolean cacheEntry, + AsciiBytes nameAlias) { + int hashCode = AsciiBytes.hashCode(name); + T entry = getEntry(hashCode, name, NO_SUFFIX, type, cacheEntry, nameAlias); + if (entry == null) { + hashCode = AsciiBytes.hashCode(hashCode, SLASH); + entry = getEntry(hashCode, name, SLASH, type, cacheEntry, nameAlias); + } + return entry; + } + + private T getEntry(int hashCode, CharSequence name, char suffix, Class type, + boolean cacheEntry, AsciiBytes nameAlias) { + int index = getFirstIndex(hashCode); + while (index >= 0 && index < this.size && this.hashCodes[index] == hashCode) { + T entry = getEntry(index, type, cacheEntry, nameAlias); + if (entry.hasName(name, suffix)) { + return entry; + } + index++; + } + return null; + } + + @SuppressWarnings("unchecked") + private T getEntry(int index, Class type, boolean cacheEntry, AsciiBytes nameAlias) { + try { + FileHeader cached = this.entriesCache.get(index); + FileHeader entry = (cached != null) ? cached : CentralDirectoryFileHeader + .fromRandomAccessData(this.centralDirectoryData, this.centralDirectoryOffsets[index], this.filter); + if (CentralDirectoryFileHeader.class.equals(entry.getClass()) && type.equals(JarEntry.class)) { + entry = new JarEntry(this.jarFile, index, (CentralDirectoryFileHeader) entry, nameAlias); + } + if (cacheEntry && cached != entry) { + this.entriesCache.put(index, entry); + } + return (T) entry; + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + private int getFirstIndex(int hashCode) { + int index = Arrays.binarySearch(this.hashCodes, 0, this.size, hashCode); + if (index < 0) { + return -1; + } + while (index > 0 && this.hashCodes[index - 1] == hashCode) { + index--; + } + return index; + } + + void clearCache() { + this.entriesCache.clear(); + } + + private AsciiBytes applyFilter(AsciiBytes name) { + return (this.filter != null) ? this.filter.apply(name) : name; + } + + JarEntryCertification getCertification(JarEntry entry) throws IOException { + JarEntryCertification[] certifications = this.certifications; + if (certifications == null) { + certifications = new JarEntryCertification[this.size]; + // We fallback to use JarInputStream to obtain the certs. This isn't that + // fast, but hopefully doesn't happen too often. + try (JarInputStream certifiedJarStream = new JarInputStream(this.jarFile.getData().getInputStream())) { + java.util.jar.JarEntry certifiedEntry = null; + while ((certifiedEntry = certifiedJarStream.getNextJarEntry()) != null) { + // Entry must be closed to trigger a read and set entry certificates + certifiedJarStream.closeEntry(); + int index = getEntryIndex(certifiedEntry.getName()); + if (index != -1) { + certifications[index] = JarEntryCertification.from(certifiedEntry); + } + } + } + this.certifications = certifications; + } + JarEntryCertification certification = certifications[entry.getIndex()]; + return (certification != null) ? certification : JarEntryCertification.NONE; + } + + private int getEntryIndex(CharSequence name) { + int hashCode = AsciiBytes.hashCode(name); + int index = getFirstIndex(hashCode); + while (index >= 0 && index < this.size && this.hashCodes[index] == hashCode) { + FileHeader candidate = getEntry(index, FileHeader.class, false, null); + if (candidate.hasName(name, NO_SUFFIX)) { + return index; + } + index++; + } + return -1; + } + + /** + * Iterator for contained entries. + */ + private final class EntryIterator implements Iterator { + + private final Runnable validator; + + private int index = 0; + + private EntryIterator(Runnable validator) { + this.validator = validator; + validator.run(); + } + + @Override + public boolean hasNext() { + this.validator.run(); + return this.index < JarFileEntries.this.size; + } + + @Override + public JarEntry next() { + this.validator.run(); + if (!hasNext()) { + throw new NoSuchElementException(); + } + int entryIndex = JarFileEntries.this.positions[this.index]; + this.index++; + return getEntry(entryIndex, JarEntry.class, false, null); + } + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java new file mode 100755 index 0000000..bc344a6 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarFileWrapper.java @@ -0,0 +1,177 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + + +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.Permission; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import com.gitee.starblues.loader.utils.ObjectUtils; + +/** + * A wrapper used to create a copy of a {@link JarFile} so that it can be safely closed + * without closing the original. + * + * @author Phillip Webb + */ +public class JarFileWrapper extends JarFile { + + private final String parentName; + + private final JarFile parent; + + private final Map> inputStreamCache; + + private final AtomicBoolean canClosed = new AtomicBoolean(false); + + JarFileWrapper(JarFile parent) throws IOException { + super(parent.getRootJarFile().getFile()); + this.parent = parent; + this.parentName = UUID.randomUUID().toString() + parent.getName(); + this.inputStreamCache = new ConcurrentHashMap<>(); + } + + @Override + public URL getUrl() throws MalformedURLException { + return this.parent.getUrl(); + } + + @Override + JarFileType getType() { + return this.parent.getType(); + } + + @Override + Permission getPermission() { + return this.parent.getPermission(); + } + + @Override + public Manifest getManifest() throws IOException { + return this.parent.getManifest(); + } + + @Override + public Enumeration entries() { + return this.parent.entries(); + } + + @Override + public Stream stream() { + return this.parent.stream(); + } + + @Override + public com.gitee.starblues.loader.jar.JarEntry getJarEntry(String name) { + return this.parent.getJarEntry(name); + } + + @Override + public ZipEntry getEntry(String name) { + return this.parent.getEntry(name); + } + + @Override + InputStream getInputStream() throws IOException { + InputStream inputStream = this.parent.getInputStream(); + addInputStream(parentName, inputStream); + return inputStream; + } + + @Override + public synchronized InputStream getInputStream(ZipEntry ze) throws IOException { + InputStream inputStream = this.parent.getInputStream(ze); + addInputStream(ze.getName(), inputStream); + return inputStream; + } + + @Override + public String getComment() { + return this.parent.getComment(); + } + + @Override + public int size() { + return this.parent.size(); + } + + @Override + public String toString() { + return this.parent.toString(); + } + + @Override + public String getName() { + return this.parent.getName(); + } + + @Override + public void close() throws IOException { + super.close(); + // Modified Added close logic + if(canClosed.get()){ + for (List inputStreams : inputStreamCache.values()) { + if(ObjectUtils.isEmpty(inputStreams)){ + continue; + } + for (InputStream inputStream : inputStreams) { + if(inputStream == null){ + continue; + } + IOUtils.closeQuietly(inputStream); + } + } + IOUtils.closeQuietly(parent); + inputStreamCache.clear(); + } + } + + public void canClosed(){ + canClosed.set(true); + } + + private void addInputStream(String name, InputStream inputStream){ + if(inputStream != null){ + List inputStreams = inputStreamCache.computeIfAbsent(name, k -> new ArrayList<>()); + inputStreams.add(inputStream); + } + } + + static JarFile unwrap(java.util.jar.JarFile jarFile) { + if (jarFile instanceof JarFile) { + return (JarFile) jarFile; + } + if (jarFile instanceof JarFileWrapper) { + return unwrap(((JarFileWrapper) jarFile).parent); + } + throw new IllegalStateException("Not a JarFile or Wrapper"); + } + +} + diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java new file mode 100755 index 0000000..5dce1fd --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/JarURLConnection.java @@ -0,0 +1,395 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import com.gitee.starblues.loader.PluginResourceStorage; +import com.gitee.starblues.loader.utils.IOUtils; + +import java.io.*; +import java.net.*; +import java.security.Permission; + +/** + * {@link java.net.JarURLConnection} used to support {@link JarFile#getUrl()}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Rostyslav Dudka + */ +public class JarURLConnection extends java.net.JarURLConnection { + + private static ThreadLocal useFastExceptions = new ThreadLocal<>(); + + private static final FileNotFoundException FILE_NOT_FOUND_EXCEPTION = new FileNotFoundException( + "Jar file or entry not found"); + + private static final IllegalStateException NOT_FOUND_CONNECTION_EXCEPTION = new IllegalStateException( + FILE_NOT_FOUND_EXCEPTION); + + private static final String SEPARATOR = "!/"; + + private static final URL EMPTY_JAR_URL; + + static { + try { + EMPTY_JAR_URL = new URL("jar:", null, 0, "file:!/", new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + // Stub URLStreamHandler to prevent the wrong JAR Handler from being + // Instantiated and cached. + return null; + } + }); + } catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + + private static final JarEntryName EMPTY_JAR_ENTRY_NAME = new JarEntryName(new StringSequence("")); + + private static final JarURLConnection NOT_FOUND_CONNECTION = JarURLConnection.notFound(); + + private final AbstractJarFile jarFile; + + private Permission permission; + + private URL jarFileUrl; + + private final JarEntryName jarEntryName; + + private java.util.jar.JarEntry jarEntry; + + private JarURLConnection(URL url, AbstractJarFile jarFile, JarEntryName jarEntryName) throws IOException { + // What we pass to super is ultimately ignored + super(EMPTY_JAR_URL); + this.url = url; + this.jarFile = jarFile; + this.jarEntryName = jarEntryName; + } + + @Override + public void connect() throws IOException { + if (this.jarFile == null) { + throw FILE_NOT_FOUND_EXCEPTION; + } + if (!this.jarEntryName.isEmpty() && this.jarEntry == null) { + this.jarEntry = this.jarFile.getJarEntry(getEntryName()); + if (this.jarEntry == null) { + throwFileNotFound(this.jarEntryName, this.jarFile); + } + } + this.connected = true; + } + + @Override + public java.util.jar.JarFile getJarFile() throws IOException { + connect(); + return this.jarFile; + } + + @Override + public URL getJarFileURL() { + if (this.jarFile == null) { + throw NOT_FOUND_CONNECTION_EXCEPTION; + } + if (this.jarFileUrl == null) { + this.jarFileUrl = buildJarFileUrl(); + } + return this.jarFileUrl; + } + + private URL buildJarFileUrl() { + try { + String spec = this.jarFile.getUrl().getFile(); + if (spec.endsWith(SEPARATOR)) { + spec = spec.substring(0, spec.length() - SEPARATOR.length()); + } + if (!spec.contains(SEPARATOR)) { + return new URL(spec); + } + return new URL("jar:" + spec); + } catch (MalformedURLException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public java.util.jar.JarEntry getJarEntry() throws IOException { + if (this.jarEntryName == null || this.jarEntryName.isEmpty()) { + return null; + } + connect(); + return this.jarEntry; + } + + @Override + public String getEntryName() { + if (this.jarFile == null) { + throw NOT_FOUND_CONNECTION_EXCEPTION; + } + return this.jarEntryName.toString(); + } + + @Override + public synchronized InputStream getInputStream() throws IOException { + if (this.jarFile == null) { + throw FILE_NOT_FOUND_EXCEPTION; + } + if (this.jarEntryName.isEmpty() && this.jarFile.getType() == JarFile.JarFileType.DIRECT) { + throw new IOException("no entry name specified"); + } + connect(); + InputStream inputStream = (this.jarEntryName.isEmpty() ? this.jarFile.getInputStream() + : this.jarFile.getInputStream(this.jarEntry)); + if (inputStream == null) { + throwFileNotFound(this.jarEntryName, this.jarFile); + return null; + } + return inputStream; + } + + private void throwFileNotFound(Object entry, AbstractJarFile jarFile) throws FileNotFoundException { + if (Boolean.TRUE.equals(useFastExceptions.get())) { + throw FILE_NOT_FOUND_EXCEPTION; + } + throw new FileNotFoundException("JAR entry " + entry + " not found in " + jarFile.getName()); + } + + @Override + public int getContentLength() { + long length = getContentLengthLong(); + if (length > Integer.MAX_VALUE) { + return -1; + } + return (int) length; + } + + @Override + public long getContentLengthLong() { + if (this.jarFile == null) { + return -1; + } + try { + if (this.jarEntryName.isEmpty()) { + return this.jarFile.size(); + } + java.util.jar.JarEntry entry = getJarEntry(); + return (entry != null) ? (int) entry.getSize() : -1; + } catch (IOException ex) { + return -1; + } + } + + @Override + public Object getContent() throws IOException { + connect(); + return this.jarEntryName.isEmpty() ? this.jarFile : super.getContent(); + } + + @Override + public String getContentType() { + return (this.jarEntryName != null) ? this.jarEntryName.getContentType() : null; + } + + @Override + public Permission getPermission() throws IOException { + if (this.jarFile == null) { + throw FILE_NOT_FOUND_EXCEPTION; + } + if (this.permission == null) { + this.permission = this.jarFile.getPermission(); + } + return this.permission; + } + + @Override + public long getLastModified() { + if (this.jarFile == null || this.jarEntryName.isEmpty()) { + return 0; + } + try { + java.util.jar.JarEntry entry = getJarEntry(); + return (entry != null) ? entry.getTime() : 0; + } catch (IOException ex) { + return 0; + } + } + + static void setUseFastExceptions(boolean useFastExceptions) { + JarURLConnection.useFastExceptions.set(useFastExceptions); + } + + static JarURLConnection get(URL url, JarFile jarFile) throws IOException { + StringSequence spec = new StringSequence(url.getFile()); + int index = indexOfRootSpec(spec, jarFile.getPathFromRoot()); + if (index == -1) { + return (Boolean.TRUE.equals(useFastExceptions.get()) ? NOT_FOUND_CONNECTION + : new JarURLConnection(url, null, EMPTY_JAR_ENTRY_NAME)); + } + int separator; + while ((separator = spec.indexOf(SEPARATOR, index)) > 0) { + JarEntryName entryName = JarEntryName.get(spec.subSequence(index, separator)); + JarEntry jarEntry = jarFile.getJarEntry(entryName.toCharSequence()); + if (jarEntry == null) { + return JarURLConnection.notFound(jarFile, entryName); + } + jarFile = jarFile.getNestedJarFile(jarEntry); + index = separator + SEPARATOR.length(); + } + JarEntryName jarEntryName = JarEntryName.get(spec, index); + if (Boolean.TRUE.equals(useFastExceptions.get()) && !jarEntryName.isEmpty() + && !jarFile.containsEntry(jarEntryName.toString())) { + return NOT_FOUND_CONNECTION; + } + JarFileWrapper jarFileWrapper = new JarFileWrapper(jarFile); + PluginResourceStorage.addJarFile(jarFileWrapper); + return new JarURLConnection(url, jarFileWrapper, jarEntryName); + } + + private static int indexOfRootSpec(StringSequence file, String pathFromRoot) { + int separatorIndex = file.indexOf(SEPARATOR); + if (separatorIndex < 0 || !file.startsWith(pathFromRoot, separatorIndex)) { + return -1; + } + return separatorIndex + SEPARATOR.length() + pathFromRoot.length(); + } + + private static JarURLConnection notFound() { + try { + return notFound(null, null); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + private static JarURLConnection notFound(JarFile jarFile, JarEntryName jarEntryName) throws IOException { + if (Boolean.TRUE.equals(useFastExceptions.get())) { + return NOT_FOUND_CONNECTION; + } + return new JarURLConnection(null, jarFile, jarEntryName); + } + + /** + * A JarEntryName parsed from a URL String. + */ + static class JarEntryName { + + private final StringSequence name; + + private String contentType; + + JarEntryName(StringSequence spec) { + this.name = decode(spec); + } + + private StringSequence decode(StringSequence source) { + if (source.isEmpty() || (source.indexOf('%') < 0)) { + return source; + } + ByteArrayOutputStream bos = null; + try { + bos = new ByteArrayOutputStream(source.length()); + write(source.toString(), bos); + // AsciiBytes is what is used to store the JarEntries so make it symmetric + return new StringSequence(AsciiBytes.toString(bos.toByteArray())); + } finally { + if(bos != null){ + IOUtils.closeQuietly(bos); + } + } + } + + private void write(String source, ByteArrayOutputStream outputStream) { + int length = source.length(); + for (int i = 0; i < length; i++) { + int c = source.charAt(i); + if (c > 127) { + try { + String encoded = URLEncoder.encode(String.valueOf((char) c), "UTF-8"); + write(encoded, outputStream); + } + catch (UnsupportedEncodingException ex) { + throw new IllegalStateException(ex); + } + } + else { + if (c == '%') { + if ((i + 2) >= length) { + throw new IllegalArgumentException( + "Invalid encoded sequence \"" + source.substring(i) + "\""); + } + c = decodeEscapeSequence(source, i); + i += 2; + } + outputStream.write(c); + } + } + } + + private char decodeEscapeSequence(String source, int i) { + int hi = Character.digit(source.charAt(i + 1), 16); + int lo = Character.digit(source.charAt(i + 2), 16); + if (hi == -1 || lo == -1) { + throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); + } + return ((char) ((hi << 4) + lo)); + } + + CharSequence toCharSequence() { + return this.name; + } + + @Override + public String toString() { + return this.name.toString(); + } + + boolean isEmpty() { + return this.name.isEmpty(); + } + + String getContentType() { + if (this.contentType == null) { + this.contentType = deduceContentType(); + } + return this.contentType; + } + + private String deduceContentType() { + // Guess the content type, don't bother with streams as mark is not supported + String type = isEmpty() ? "x-java/jar" : null; + type = (type != null) ? type : guessContentTypeFromName(toString()); + type = (type != null) ? type : "content/unknown"; + return type; + } + + static JarEntryName get(StringSequence spec) { + return get(spec, 0); + } + + static JarEntryName get(StringSequence spec, int beginIndex) { + if (spec.length() <= beginIndex) { + return EMPTY_JAR_ENTRY_NAME; + } + return new JarEntryName(spec.subSequence(beginIndex)); + } + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java new file mode 100755 index 0000000..1c25ec0 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessData.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * Interface that provides read-only random access to some underlying data. + * Implementations must allow concurrent reads in a thread-safe manner. + * + * @author Phillip Webb + * @since 1.0.0 + */ +public interface RandomAccessData { + + + /** + * Returns an {@link InputStream} that can be used to read the underlying data. The + * caller is responsible close the underlying stream. + * @return a new input stream that can be used to read the underlying data. + * @throws IOException if the stream cannot be opened + */ + InputStream getInputStream() throws IOException; + + /** + * Returns a new {@link RandomAccessData} for a specific subsection of this data. + * @param offset the offset of the subsection + * @param length the length of the subsection + * @return the subsection data + */ + RandomAccessData getSubsection(long offset, long length); + + /** + * Reads all the data and returns it as a byte array. + * @return the data + * @throws IOException if the data cannot be read + */ + byte[] read() throws IOException; + + /** + * Reads the {@code length} bytes of data starting at the given {@code offset}. + * @param offset the offset from which data should be read + * @param length the number of bytes to be read + * @return the data + * @throws IOException if the data cannot be read + * @throws IndexOutOfBoundsException if offset is beyond the end of the file or + * subsection + * @throws EOFException if offset plus length is greater than the length of the file + * or subsection + */ + byte[] read(long offset, long length) throws IOException; + + /** + * Returns the size of the data. + * @return the size + */ + long getSize(); + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java new file mode 100755 index 0000000..7f671af --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/RandomAccessDataFile.java @@ -0,0 +1,253 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.*; + +/** + * {@link RandomAccessData} implementation backed by a {@link RandomAccessFile}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @since 1.0.0 + */ +public class RandomAccessDataFile implements RandomAccessData{ + + private final FileAccess fileAccess; + + private final long offset; + + private final long length; + + /** + * Create a new {@link RandomAccessDataFile} backed by the specified file. + * @param file the underlying file + * @throws IllegalArgumentException if the file is null or does not exist + */ + public RandomAccessDataFile(File file) { + if (file == null) { + throw new IllegalArgumentException("File must not be null"); + } + this.fileAccess = new FileAccess(file); + this.offset = 0L; + this.length = file.length(); + } + + /** + * Private constructor used to create a {@link #getSubsection(long, long) subsection}. + * @param fileAccess provides access to the underlying file + * @param offset the offset of the section + * @param length the length of the section + */ + private RandomAccessDataFile(FileAccess fileAccess, long offset, long length) { + this.fileAccess = fileAccess; + this.offset = offset; + this.length = length; + } + + /** + * Returns the underlying File. + * @return the underlying file + */ + public File getFile() { + return this.fileAccess.file; + } + + @Override + public InputStream getInputStream() throws IOException { + return new DataInputStream(); + } + + @Override + public RandomAccessData getSubsection(long offset, long length) { + if (offset < 0 || length < 0 || offset + length > this.length) { + throw new IndexOutOfBoundsException(); + } + return new RandomAccessDataFile(this.fileAccess, this.offset + offset, length); + } + + @Override + public byte[] read() throws IOException { + return read(0, this.length); + } + + @Override + public byte[] read(long offset, long length) throws IOException { + if (offset > this.length) { + throw new IndexOutOfBoundsException(); + } + if (offset + length > this.length) { + throw new EOFException(); + } + byte[] bytes = new byte[(int) length]; + read(bytes, offset, 0, bytes.length); + return bytes; + } + + private int readByte(long position) throws IOException { + if (position >= this.length) { + return -1; + } + return this.fileAccess.readByte(this.offset + position); + } + + private int read(byte[] bytes, long position, int offset, int length) throws IOException { + if (position > this.length) { + return -1; + } + return this.fileAccess.read(bytes, this.offset + position, offset, length); + } + + @Override + public long getSize() { + return this.length; + } + + public void close() throws IOException { + this.fileAccess.close(); + } + + /** + * {@link InputStream} implementation for the {@link RandomAccessDataFile}. + */ + private class DataInputStream extends InputStream { + + private int position; + + @Override + public int read() throws IOException { + int read = RandomAccessDataFile.this.readByte(this.position); + if (read > -1) { + moveOn(1); + } + return read; + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, (b != null) ? b.length : 0); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException("Bytes must not be null"); + } + return doRead(b, off, len); + } + + /** + * Perform the actual read. + * @param b the bytes to read or {@code null} when reading a single byte + * @param off the offset of the byte array + * @param len the length of data to read + * @return the number of bytes read into {@code b} or the actual read byte if + * {@code b} is {@code null}. Returns -1 when the end of the stream is reached + * @throws IOException in case of I/O errors + */ + int doRead(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return 0; + } + int cappedLen = cap(len); + if (cappedLen <= 0) { + return -1; + } + return (int) moveOn(RandomAccessDataFile.this.read(b, this.position, off, cappedLen)); + } + + @Override + public long skip(long n) throws IOException { + return (n <= 0) ? 0 : moveOn(cap(n)); + } + + /** + * Cap the specified value such that it cannot exceed the number of bytes + * remaining. + * @param n the value to cap + * @return the capped value + */ + private int cap(long n) { + return (int) Math.min(RandomAccessDataFile.this.length - this.position, n); + } + + /** + * Move the stream position forwards the specified amount. + * @param amount the amount to move + * @return the amount moved + */ + private long moveOn(int amount) { + this.position += amount; + return amount; + } + + } + + private static final class FileAccess { + + private final Object monitor = new Object(); + + private final File file; + + private RandomAccessFile randomAccessFile; + + private FileAccess(File file) { + this.file = file; + openIfNecessary(); + } + + private int read(byte[] bytes, long position, int offset, int length) throws IOException { + synchronized (this.monitor) { + openIfNecessary(); + this.randomAccessFile.seek(position); + return this.randomAccessFile.read(bytes, offset, length); + } + } + + private void openIfNecessary() { + if (this.randomAccessFile == null) { + try { + this.randomAccessFile = new RandomAccessFile(this.file, "r"); + } catch (FileNotFoundException ex) { + throw new IllegalArgumentException( + String.format("File %s must exist", this.file.getAbsolutePath())); + } + } + } + + private void close() throws IOException { + synchronized (this.monitor) { + if (this.randomAccessFile != null) { + this.randomAccessFile.close(); + this.randomAccessFile = null; + } + } + } + + private int readByte(long position) throws IOException { + synchronized (this.monitor) { + openIfNecessary(); + this.randomAccessFile.seek(position); + return this.randomAccessFile.read(); + } + } + + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java new file mode 100755 index 0000000..7b2c1f7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/StringSequence.java @@ -0,0 +1,159 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.util.Objects; + +/** + * A {@link CharSequence} backed by a single shared {@link String}. Unlike a regular + * {@link String}, {@link #subSequence(int, int)} operations will not copy the underlying + * character array. + * + * @author Phillip Webb + */ +public class StringSequence implements CharSequence { + + private final String source; + + private final int start; + + private final int end; + + private int hash; + + StringSequence(String source) { + this(source, 0, (source != null) ? source.length() : -1); + } + + StringSequence(String source, int start, int end) { + Objects.requireNonNull(source, "Source must not be null"); + if (start < 0) { + throw new StringIndexOutOfBoundsException(start); + } + if (end > source.length()) { + throw new StringIndexOutOfBoundsException(end); + } + this.source = source; + this.start = start; + this.end = end; + } + + StringSequence subSequence(int start) { + return subSequence(start, length()); + } + + @Override + public StringSequence subSequence(int start, int end) { + int subSequenceStart = this.start + start; + int subSequenceEnd = this.start + end; + if (subSequenceStart > this.end) { + throw new StringIndexOutOfBoundsException(start); + } + if (subSequenceEnd > this.end) { + throw new StringIndexOutOfBoundsException(end); + } + if (start == 0 && subSequenceEnd == this.end) { + return this; + } + return new StringSequence(this.source, subSequenceStart, subSequenceEnd); + } + + /** + * Returns {@code true} if the sequence is empty. Public to be compatible with JDK 15. + * @return {@code true} if {@link #length()} is {@code 0}, otherwise {@code false} + */ + public boolean isEmpty() { + return length() == 0; + } + + @Override + public int length() { + return this.end - this.start; + } + + @Override + public char charAt(int index) { + return this.source.charAt(this.start + index); + } + + int indexOf(char ch) { + return this.source.indexOf(ch, this.start) - this.start; + } + + int indexOf(String str) { + return this.source.indexOf(str, this.start) - this.start; + } + + int indexOf(String str, int fromIndex) { + return this.source.indexOf(str, this.start + fromIndex) - this.start; + } + + boolean startsWith(String prefix) { + return startsWith(prefix, 0); + } + + boolean startsWith(String prefix, int offset) { + int prefixLength = prefix.length(); + int length = length(); + if (length - prefixLength - offset < 0) { + return false; + } + return this.source.startsWith(prefix, this.start + offset); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof CharSequence)) { + return false; + } + CharSequence other = (CharSequence) obj; + int n = length(); + if (n != other.length()) { + return false; + } + int i = 0; + while (n-- != 0) { + if (charAt(i) != other.charAt(i)) { + return false; + } + i++; + } + return true; + } + + @Override + public int hashCode() { + int hash = this.hash; + if (hash == 0 && length() > 0) { + for (int i = this.start; i < this.end; i++) { + hash = 31 * hash + this.source.charAt(i); + } + this.hash = hash; + } + return hash; + } + + @Override + public String toString() { + return this.source.substring(this.start, this.end); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java new file mode 100755 index 0000000..e4192f1 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/jar/ZipInflaterInputStream.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2021 the original author or authors. + * Copy from spring-boot-loader + * + * Licensed 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 + * + * https://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 com.gitee.starblues.loader.jar; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * {@link InflaterInputStream} that supports the writing of an extra "dummy" byte (which + * is required with JDK 6) and returns accurate available() results. + * + * @author Phillip Webb + */ +public class ZipInflaterInputStream extends InflaterInputStream { + + private int available; + + private boolean extraBytesWritten; + + ZipInflaterInputStream(InputStream inputStream, int size) { + super(inputStream, new Inflater(true), getInflaterBufferSize(size)); + this.available = size; + } + + @Override + public int available() throws IOException { + if (this.available < 0) { + return super.available(); + } + return this.available; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int result = super.read(b, off, len); + if (result != -1) { + this.available -= result; + } + return result; + } + + @Override + public void close() throws IOException { + super.close(); + this.inf.end(); + } + + @Override + protected void fill() throws IOException { + try { + super.fill(); + } catch (EOFException ex) { + if (this.extraBytesWritten) { + throw ex; + } + this.len = 1; + this.buf[0] = 0x0; + this.extraBytesWritten = true; + this.inf.setInput(this.buf, 0, this.len); + } + } + + private static int getInflaterBufferSize(long size) { + // inflater likes some space + size += 2; + size = (size > 65536) ? 8192 : size; + size = (size <= 0) ? 4096 : size; + return (int) size; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java new file mode 100755 index 0000000..e1dc6d5 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractLauncher.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +/** + * 抽象的启动引导者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public abstract class AbstractLauncher implements Launcher { + + @Override + public R run(String... args) throws Exception { + ClassLoader classLoader = createClassLoader(args); + Thread thread = Thread.currentThread(); + ClassLoader oldClassLoader = thread.getContextClassLoader(); + try { + thread.setContextClassLoader(classLoader); + return launch(classLoader, args); + } finally { + thread.setContextClassLoader(oldClassLoader); + } + } + + /** + * 创建classloader + * @param args 参数 + * @return ClassLoader + * @throws Exception 创建异常 + */ + protected abstract ClassLoader createClassLoader(String... args) throws Exception; + + /** + * 子类实现具体的启动方法 + * @param classLoader 当前的 ClassLoader + * @param args 启动参数 + * @return 启动返回值 + * @throws Exception 启动异常 + */ + protected abstract R launch(ClassLoader classLoader, String... args) throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractMainLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractMainLauncher.java new file mode 100755 index 0000000..0b1adcb --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/AbstractMainLauncher.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.DevelopmentMode; + +/** + * 抽象的启动引导者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public abstract class AbstractMainLauncher extends AbstractLauncher { + + public static final String MAIN_CLASS_LOADER_NAME = "MainProgramLauncherClassLoader"; + + /** + * SpringPluginBootstrap 包名称 + * @since 3.0.4 + */ + private final static String SPRING_PLUGIN_BOOTSTRAP_PACKAGE_NAME = "com.gitee.starblues.bootstrap.SpringPluginBootstrap"; + + /** + * SpringPluginBootstrap 依赖坐标 + * @since 3.0.4 + */ + private final static String SPRING_PLUGIN_BOOTSTRAP_COORDINATE = "com.gitee.starblues:spring-brick-bootstrap"; + + + @Override + public ClassLoader run(String... args) throws Exception { + ClassLoader classLoader = createClassLoader(args); + if(!resolveThreadClassLoader()){ + return toLaunch(classLoader, args); + } + Thread thread = Thread.currentThread(); + ClassLoader oldClassLoader = thread.getContextClassLoader(); + try { + thread.setContextClassLoader(classLoader); + return toLaunch(classLoader, args); + } finally { + thread.setContextClassLoader(oldClassLoader); + } + } + + protected ClassLoader toLaunch(ClassLoader classLoader, String... args) throws Exception{ + LauncherContext.setMainClassLoader(classLoader); + checkSpringPluginBootstrap(classLoader); + return launch(classLoader, args); + } + + protected boolean resolveThreadClassLoader(){ + return true; + } + + /** + * 检查 {@link this#SPRING_PLUGIN_BOOTSTRAP_COORDINATE} 依赖是否配置合适 + * @param classLoader 当前主程序classloader + * @throws RuntimeException 检查一次 + */ + private void checkSpringPluginBootstrap(ClassLoader classLoader) throws RuntimeException{ + try { + classLoader.loadClass(SPRING_PLUGIN_BOOTSTRAP_PACKAGE_NAME); + if(DevelopmentModeSetting.isolation()){ + // 主程序加载到了 + throw new RuntimeException("[" + DevelopmentMode.ISOLATION + "]模式下" + + "不能将[" + SPRING_PLUGIN_BOOTSTRAP_COORDINATE + "]依赖定义到主程序中, 只能依赖到插件中!"); + } + } catch (ClassNotFoundException e) { + if(!DevelopmentModeSetting.isolation()){ + throw new RuntimeException("[" + DevelopmentMode.COEXIST + "]模式" + + "需要将[" + SPRING_PLUGIN_BOOTSTRAP_COORDINATE + "]依赖定义到主程序中!"); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevLauncher.java new file mode 100755 index 0000000..170bdf1 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevLauncher.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.launcher.coexist.CoexistBaseLauncher; +import com.gitee.starblues.loader.launcher.isolation.IsolationBaseLauncher; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import lombok.AllArgsConstructor; + +/** + * 开发环境 Launcher + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +@AllArgsConstructor +public class DevLauncher implements Launcher{ + + private final SpringBootstrap springBootstrap; + + @Override + public ClassLoader run(String... args) throws Exception { + MethodRunner methodRunner = new MethodRunner(springBootstrap.getClass().getName(), + SPRING_BOOTSTRAP_RUN_METHOD, args); + AbstractMainLauncher launcher; + if(DevelopmentModeSetting.isolation()){ + launcher = new IsolationBaseLauncher(methodRunner); + } else if(DevelopmentModeSetting.coexist()) { + launcher = new CoexistBaseLauncher(methodRunner); + } else { + throw DevelopmentModeSetting.getUnknownModeException(); + } + return launcher.run(args); + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevelopmentModeSetting.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevelopmentModeSetting.java new file mode 100755 index 0000000..439cdb5 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/DevelopmentModeSetting.java @@ -0,0 +1,72 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.DevelopmentMode; + +/** + * DevelopmentMode 设置者 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class DevelopmentModeSetting { + + private static String developmentMode; + + static void setDevelopmentMode(String developmentMode) { + DevelopmentModeSetting.developmentMode = checkModeKey(developmentMode); + } + + public static boolean isolation(){ + return DevelopmentMode.ISOLATION.equalsIgnoreCase(developmentMode); + } + + public static boolean coexist(){ + return DevelopmentMode.COEXIST.equalsIgnoreCase(developmentMode); + } + + public static String getDevelopmentMode(){ + return developmentMode; + } + + public static IllegalStateException getUnknownModeException(){ + return getUnknownModeException(null); + } + + public static IllegalStateException getUnknownModeException(String developmentMode){ + if(developmentMode == null || "".equals(developmentMode)){ + developmentMode = DevelopmentModeSetting.developmentMode; + } + return new IllegalStateException("不支持开发模式:" + developmentMode); + } + + private static String checkModeKey(String developmentMode){ + if(developmentMode == null || "".equals(developmentMode)){ + throw new RuntimeException("developmentMode 设置不能为空"); + } + if(DevelopmentMode.ISOLATION.equalsIgnoreCase(developmentMode)){ + return developmentMode; + } else if(DevelopmentMode.COEXIST.equalsIgnoreCase(developmentMode)){ + return developmentMode; + } else { + throw getUnknownModeException(developmentMode); + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java new file mode 100755 index 0000000..5739bcf --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/Launcher.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +/** + * 启动引导器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface Launcher { + + String SPRING_BOOTSTRAP_RUN_METHOD = "run"; + + /** + * 启动运行 + * @param args 启动传入的参数 + * @return 启动后的返回值 + * @throws Exception 启动异常 + */ + R run(String... args) throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/LauncherContext.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/LauncherContext.java new file mode 100755 index 0000000..6ab8859 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/LauncherContext.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +/** + * 启动上下文 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public class LauncherContext { + + private static volatile ClassLoader mainClassLoader = null; + + /** + * 获取主程序的ClassLoader + * @return 主程序ClassLoader + */ + public static ClassLoader getMainClassLoader(){ + return mainClassLoader; + } + + /** + * 设置主程序的ClassLoader + * @param classLoader 主程序ClassLoader + */ + static void setMainClassLoader(ClassLoader classLoader){ + if(mainClassLoader == null){ + synchronized (AbstractLauncher.class){ + if(mainClassLoader == null){ + mainClassLoader = classLoader; + } + } + } + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ProdLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ProdLauncher.java new file mode 100755 index 0000000..09bbce7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/ProdLauncher.java @@ -0,0 +1,128 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.jar.JarFile; +import com.gitee.starblues.loader.launcher.coexist.CoexistFastJarLauncher; +import com.gitee.starblues.loader.launcher.coexist.CoexistJarOuterLauncher; +import com.gitee.starblues.loader.launcher.isolation.IsolationFastJarLauncher; +import com.gitee.starblues.loader.launcher.isolation.IsolationJarOuterLauncher; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import com.gitee.starblues.loader.utils.ObjectUtils; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.Objects; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.loader.LoaderConstant.*; + +/** + * 生产环境 Launcher + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class ProdLauncher implements Launcher{ + + static final String SPRING_BOOTSTRAP_RUN_METHOD = "run"; + + @Override + public ClassLoader run(String... args) throws Exception { + File rootJarFile = getRootJarFile(); + String startClass = null; + String mainPackageType; + String developmentMode; + try (JarFile jarFile = new JarFile(rootJarFile)){ + Manifest manifest = jarFile.getManifest(); + IllegalStateException exception = new IllegalStateException("当前启动包非法包!"); + if(manifest == null || manifest.getMainAttributes() == null){ + throw exception; + } + Attributes mainAttributes = manifest.getMainAttributes(); + startClass = mainAttributes.getValue(START_CLASS); + if (ObjectUtils.isEmpty(startClass)) { + throw exception; + } + mainPackageType = mainAttributes.getValue(MAIN_PACKAGE_TYPE); + developmentMode = mainAttributes.getValue(MAIN_DEVELOPMENT_MODE); + } + + if(ObjectUtils.isEmpty(developmentMode)){ + throw new RuntimeException("未发现 developmentMode 配置"); + } + DevelopmentModeSetting.setDevelopmentMode(developmentMode); + + MethodRunner methodRunner = new MethodRunner(startClass, SPRING_BOOTSTRAP_RUN_METHOD, args); + AbstractMainLauncher launcher; + + if(Objects.equals(mainPackageType, MAIN_PACKAGE_TYPE_JAR_OUTER)){ + launcher = getJarOuterLauncher(methodRunner, rootJarFile); + } else { + launcher = getFastJarLauncher(methodRunner, rootJarFile); + } + + return launcher.run(args); + } + + + private File getRootJarFile() throws URISyntaxException { + ProtectionDomain protectionDomain = SpringMainBootstrap.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; + String path = (location != null) ? location.getSchemeSpecificPart() : null; + if (path == null) { + throw new IllegalStateException("Unable to determine code source archive"); + } + File root = new File(path); + if (!root.exists()) { + throw new IllegalStateException("Unable to determine code source archive from " + root); + } + return root; + } + + private AbstractMainLauncher getFastJarLauncher(MethodRunner methodRunner, File rootJarFile){ + AbstractMainLauncher launcher; + if(DevelopmentModeSetting.isolation()){ + launcher = new IsolationFastJarLauncher(methodRunner, rootJarFile); + } else if(DevelopmentModeSetting.coexist()){ + launcher = new CoexistFastJarLauncher(methodRunner, rootJarFile); + } else { + throw DevelopmentModeSetting.getUnknownModeException(); + } + return launcher; + } + + private AbstractMainLauncher getJarOuterLauncher(MethodRunner methodRunner, File rootJarFile){ + AbstractMainLauncher launcher; + if(DevelopmentModeSetting.isolation()){ + launcher = new IsolationJarOuterLauncher(methodRunner, rootJarFile); + } else if(DevelopmentModeSetting.coexist()){ + launcher = new CoexistJarOuterLauncher(methodRunner, rootJarFile); + } else { + throw DevelopmentModeSetting.getUnknownModeException(); + } + return launcher; + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java new file mode 100755 index 0000000..9335b07 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringBootstrap.java @@ -0,0 +1,47 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.DevelopmentMode; + +/** + * 主程序实现该接口引导启动SpringBoot + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface SpringBootstrap { + + /** + * 启动 + * @param args 启动参数 + * @throws Exception 启动异常 + */ + void run(String[] args) throws Exception; + + + /** + * 设置开发模式 + * + * @return DevelopmentMode + */ + default String developmentMode(){ + return DevelopmentMode.ISOLATION; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java new file mode 100755 index 0000000..d627ec8 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainBootstrap.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.jar.JarFile; +import com.gitee.starblues.loader.launcher.runner.MainMethodRunner; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.util.Objects; +import java.util.concurrent.CountDownLatch; + +/** + * 主程序引导器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class SpringMainBootstrap { + + static final String MAIN_RUN_METHOD = "main"; + + private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(1); + + private static SpringBootstrap springBootstrap; + + public static void launch(Class bootstrapClass, String[] args) { + try { + SpringBootstrap springBootstrap = bootstrapClass.getConstructor().newInstance(); + launch(springBootstrap, args); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void launch(SpringBootstrap springBootstrap, String[] args) { + SpringMainBootstrap.springBootstrap = Objects.requireNonNull(springBootstrap, "springBootBootstrap 不能为空"); + DevelopmentModeSetting.setDevelopmentMode(springBootstrap.developmentMode()); + MainMethodRunner mainMethodRunner = new MainMethodRunner(SpringMainBootstrap.class.getName(), + MAIN_RUN_METHOD, args); + JarFile.registerUrlProtocolHandler(); + Thread launchThread = new Thread(new Runner(mainMethodRunner)); + launchThread.start(); + try { + COUNT_DOWN_LATCH.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static class Runner implements Runnable{ + + private final MethodRunner methodRunner; + + public Runner(MethodRunner methodRunner) { + this.methodRunner = methodRunner; + } + + @Override + public void run() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + try { + methodRunner.run(contextClassLoader); + } catch (Exception e) { + e.printStackTrace(); + } finally { + COUNT_DOWN_LATCH.countDown(); + } + } + } + + private static void main(String[] args) throws Exception { + Objects.requireNonNull(springBootstrap, "springBootBootstrap 不能为空"); + Launcher launcher = new DevLauncher(springBootstrap); + launcher.run(args); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java new file mode 100755 index 0000000..b47de02 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/SpringMainProdBootstrap.java @@ -0,0 +1,40 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher; + +import com.gitee.starblues.loader.jar.JarFile; + +/** + * 主程序生成环境启动引导器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class SpringMainProdBootstrap { + + public static void main(String[] args) throws Exception { + JarFile.registerUrlProtocolHandler(); + new SpringMainProdBootstrap().run(args); + } + + private void run(String[] args) throws Exception{ + Launcher launcher = new ProdLauncher(); + launcher.run(args); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/ClasspathResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/ClasspathResource.java new file mode 100755 index 0000000..a96cb2e --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/ClasspathResource.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.classpath; + + +import java.net.URL; +import java.util.List; + +/** + * 获取classpath资源路径 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public interface ClasspathResource { + + /** + * 获取 classpath url 集合 + * @return List + * @throws Exception 获取异常 + */ + List getClasspath() throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/FastJarClasspathResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/FastJarClasspathResource.java new file mode 100755 index 0000000..b7dcaf2 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/FastJarClasspathResource.java @@ -0,0 +1,74 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.classpath; + +import com.gitee.starblues.loader.archive.Archive; +import com.gitee.starblues.loader.archive.ExplodedArchive; +import com.gitee.starblues.loader.archive.JarFileArchive; +import lombok.AllArgsConstructor; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static com.gitee.starblues.loader.LoaderConstant.PROD_CLASSES_PATH; +import static com.gitee.starblues.loader.LoaderConstant.PROD_LIB_PATH; + +/** + * fast jar 类型的 classpath 获取者 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +@AllArgsConstructor +public class FastJarClasspathResource implements ClasspathResource{ + + private final static Archive.EntryFilter ENTRY_FILTER = (entry)->{ + String name = entry.getName(); + return name.startsWith(PROD_CLASSES_PATH) || name.startsWith(PROD_LIB_PATH); + }; + + private final static Archive.EntryFilter INCLUDE_FILTER = (entry) -> { + if (entry.isDirectory()) { + return entry.getName().equals(PROD_CLASSES_PATH); + } + return entry.getName().startsWith(PROD_LIB_PATH); + }; + + private final File rootJarFile; + + @Override + public List getClasspath() throws Exception{ + Archive archive = getArchive(); + Iterator archiveIterator = archive.getNestedArchives(ENTRY_FILTER, INCLUDE_FILTER); + List urls = new ArrayList<>(); + while (archiveIterator.hasNext()){ + URL url = archiveIterator.next().getUrl(); + urls.add(url); + } + return urls; + } + + private Archive getArchive() throws IOException { + return (rootJarFile.isDirectory() ? new ExplodedArchive(rootJarFile) : new JarFileArchive(rootJarFile)); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/JarOutClasspathResource.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/JarOutClasspathResource.java new file mode 100755 index 0000000..31f0efe --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/classpath/JarOutClasspathResource.java @@ -0,0 +1,118 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.classpath; + +import com.gitee.starblues.loader.archive.Archive; +import com.gitee.starblues.loader.archive.ExplodedArchive; +import com.gitee.starblues.loader.archive.JarFileArchive; +import com.gitee.starblues.loader.utils.FilesUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; +import lombok.AllArgsConstructor; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.net.URL; +import java.util.*; +import java.util.jar.Manifest; + +import static com.gitee.starblues.loader.LoaderConstant.*; + +/** + * jar out 类型的 classpath 获取者 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.2 + */ +@AllArgsConstructor +public class JarOutClasspathResource implements ClasspathResource{ + + + private final static Archive.EntryFilter ENTRY_FILTER = (entry)->{ + String name = entry.getName(); + return name.startsWith(PROD_CLASSES_PATH); + }; + + private final static Archive.EntryFilter INCLUDE_FILTER = (entry) -> { + if (entry.isDirectory()) { + return entry.getName().equals(PROD_CLASSES_PATH); + } + return false; + }; + + private final File rootJarFile; + + + @Override + public List getClasspath() throws Exception { + Archive archive = getArchive(); + Iterator archives = archive.getNestedArchives(ENTRY_FILTER, INCLUDE_FILTER); + List urls = getEntryResource(archives); + urls.addAll(getLibResource(archive)); + return urls; + } + + private Archive getArchive() throws IOException { + return (rootJarFile.isDirectory() ? new ExplodedArchive(rootJarFile) : new JarFileArchive(rootJarFile)); + } + + private List getEntryResource(Iterator archives) throws Exception{ + List urls = new ArrayList<>(); + while (archives.hasNext()){ + Archive archive = archives.next(); + URL url = archive.getUrl(); + String path = url.getPath(); + if(path.contains(PROD_CLASSES_URL_SIGN)){ + urls.add(url); + } + } + return urls; + } + + private List getLibResource(Archive archive) throws Exception{ + Manifest manifest = archive.getManifest(); + String libDir = manifest.getMainAttributes().getValue(MAIN_LIB_DIR); + String relativePath = rootJarFile.isDirectory() ? rootJarFile.getPath() : rootJarFile.getParent(); + libDir = FilesUtils.resolveRelativePath(relativePath, libDir); + File libJarDir = new File(libDir); + if(!libJarDir.exists()){ + throw new IllegalStateException("主程序依赖目录不存在: " + libDir); + } + File[] libJarFile = getLibJarFile(libJarDir); + List urls = new ArrayList<>(libJarFile.length); + for (File file : libJarFile) { + urls.add(file.toPath().toUri().toURL()); + } + return urls; + } + + private File[] getLibJarFile(File rootFile) { + File[] listFiles = rootFile.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return ResourceUtils.isJarFile(pathname); + } + }); + if(listFiles == null || listFiles.length == 0){ + return new File[0]; + } + return listFiles; + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistBaseLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistBaseLauncher.java new file mode 100755 index 0000000..311af4d --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistBaseLauncher.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.coexist; + +import com.gitee.starblues.loader.classloader.GeneralUrlClassLoader; +import com.gitee.starblues.loader.launcher.AbstractMainLauncher; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + + +/** + * coexist 模式 launcher + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +public class CoexistBaseLauncher extends AbstractMainLauncher { + + private final MethodRunner methodRunner; + + public CoexistBaseLauncher(MethodRunner methodRunner) { + this.methodRunner = methodRunner; + } + + @Override + protected ClassLoader createClassLoader(String... args) throws Exception { + GeneralUrlClassLoader urlClassLoader = new GeneralUrlClassLoader(MAIN_CLASS_LOADER_NAME, + this.getClass().getClassLoader()); + addResource(urlClassLoader); + return urlClassLoader; + } + + @Override + protected ClassLoader launch(ClassLoader classLoader, String... args) throws Exception { + methodRunner.run(classLoader); + return classLoader; + } + + protected void addResource(GeneralUrlClassLoader classLoader) throws Exception{ + + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistFastJarLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistFastJarLauncher.java new file mode 100755 index 0000000..8750fe9 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistFastJarLauncher.java @@ -0,0 +1,61 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.coexist; + +import com.gitee.starblues.loader.classloader.GeneralUrlClassLoader; +import com.gitee.starblues.loader.launcher.classpath.ClasspathResource; +import com.gitee.starblues.loader.launcher.classpath.FastJarClasspathResource; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.io.File; +import java.net.URL; +import java.util.List; +import java.util.Objects; + +/** + * 主程序jar in jar 模式启动者 + * + * @author starBlues + * @since 3.0.2 + * @version 3.0.2 + */ +public class CoexistFastJarLauncher extends CoexistBaseLauncher { + + private final ClasspathResource classpathResource; + + public CoexistFastJarLauncher(MethodRunner methodRunner, File rootJarFile) { + super(methodRunner); + Objects.requireNonNull(rootJarFile, "参数 rootJarFile 不能为空"); + this.classpathResource = new FastJarClasspathResource(rootJarFile); + } + + @Override + protected boolean resolveThreadClassLoader() { + return true; + } + + @Override + protected void addResource(GeneralUrlClassLoader classLoader) throws Exception { + super.addResource(classLoader); + List classpath = classpathResource.getClasspath(); + for (URL url : classpath) { + classLoader.addResource(url); + } + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistJarOuterLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistJarOuterLauncher.java new file mode 100755 index 0000000..9ceb5ab --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/coexist/CoexistJarOuterLauncher.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.coexist; + +import com.gitee.starblues.loader.classloader.GeneralUrlClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.MainJarResourceLoader; +import com.gitee.starblues.loader.launcher.classpath.ClasspathResource; +import com.gitee.starblues.loader.launcher.classpath.JarOutClasspathResource; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.io.File; +import java.net.URL; +import java.util.List; +import java.util.Objects; + +import static com.gitee.starblues.loader.LoaderConstant.PROD_CLASSES_URL_SIGN; + + +/** + * 主程序jar-outer 模式启动者 + * + * @author starBlues + * @since 3.1.2 + * @version 3.0.2 + */ +public class CoexistJarOuterLauncher extends CoexistBaseLauncher { + + private final ClasspathResource classpathResource; + + public CoexistJarOuterLauncher(MethodRunner methodRunner, File rootJarFile) { + super(methodRunner); + Objects.requireNonNull(rootJarFile, "参数 rootJarFile 不能为空"); + this.classpathResource = new JarOutClasspathResource(rootJarFile); + } + + @Override + protected boolean resolveThreadClassLoader() { + return true; + } + + @Override + protected void addResource(GeneralUrlClassLoader classLoader) throws Exception { + super.addResource(classLoader); + List classpath = classpathResource.getClasspath(); + for (URL url : classpath) { + String path = url.getPath(); + if(path.contains(PROD_CLASSES_URL_SIGN)){ + classLoader.addResource(new MainJarResourceLoader(url)); + } else { + classLoader.addResource(url); + } + } + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationBaseLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationBaseLauncher.java new file mode 100755 index 0000000..75341e7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationBaseLauncher.java @@ -0,0 +1,98 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.isolation; + +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.launcher.AbstractMainLauncher; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import com.gitee.starblues.loader.utils.ObjectUtils; +import com.gitee.starblues.loader.utils.ResourceUtils; + + +import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * 主程序启动者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class IsolationBaseLauncher extends AbstractMainLauncher { + + private final MethodRunner methodRunner; + + public IsolationBaseLauncher(MethodRunner methodRunner) { + this.methodRunner = methodRunner; + } + + @Override + protected boolean resolveThreadClassLoader() { + return false; + } + + @Override + protected ClassLoader createClassLoader(String... args) throws Exception { + return new GenericClassLoader(MAIN_CLASS_LOADER_NAME, getParentClassLoader(), + getResourceLoaderFactory(args)); + } + + @Override + protected ClassLoader launch(ClassLoader classLoader, String... args) throws Exception { + methodRunner.run(classLoader); + ResourceUtils.release(classLoader); + return classLoader; + } + + protected ResourceLoaderFactory getResourceLoaderFactory(String... args){ + return ResourceLoaderFactoryGetter.get(MAIN_CLASS_LOADER_NAME, args); + } + + protected ClassLoader getParentClassLoader(){ + return IsolationBaseLauncher.class.getClassLoader(); + } + + protected Set getBaseResource() { + Set urls = new HashSet<>(); + String classPath = ManagementFactory.getRuntimeMXBean().getClassPath(); + if(!ObjectUtils.isEmpty(classPath)){ + String[] classPathStr = classPath.split(";"); + for (String path : classPathStr) { + try { + urls.add(new URL(path)); + } catch (MalformedURLException e) { + // 忽略 + } + } + } + ClassLoader sourceClassLoader = Thread.currentThread().getContextClassLoader(); + if(sourceClassLoader instanceof URLClassLoader){ + URLClassLoader urlClassLoader = (URLClassLoader) sourceClassLoader; + final URL[] urLs = urlClassLoader.getURLs(); + urls.addAll(Arrays.asList(urLs)); + } + return urls; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationFastJarLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationFastJarLauncher.java new file mode 100755 index 0000000..de16b58 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationFastJarLauncher.java @@ -0,0 +1,80 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.isolation; + +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.JarResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.MainJarResourceLoader; +import com.gitee.starblues.loader.launcher.classpath.ClasspathResource; +import com.gitee.starblues.loader.launcher.classpath.FastJarClasspathResource; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.io.File; +import java.net.URL; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static com.gitee.starblues.loader.LoaderConstant.*; + +/** + * 主程序jar in jar 模式启动者 + * + * @author starBlues + * @since 3.0.2 + * @version 3.0.2 + */ +public class IsolationFastJarLauncher extends IsolationBaseLauncher { + + private final ClasspathResource classpathResource; + + public IsolationFastJarLauncher(MethodRunner methodRunner, File rootJarFile) { + super(methodRunner); + Objects.requireNonNull(rootJarFile, "参数 rootJarFile 不能为空"); + this.classpathResource = new FastJarClasspathResource(rootJarFile); + } + + @Override + protected ClassLoader createClassLoader(String... args) throws Exception { + GenericClassLoader classLoader = (GenericClassLoader) super.createClassLoader(args); + addResource(classLoader); + return classLoader; + } + + @Override + protected boolean resolveThreadClassLoader() { + return true; + } + + private void addResource(GenericClassLoader classLoader) throws Exception { + Set baseResource = getBaseResource(); + List classpath = classpathResource.getClasspath(); + if(classpath != null){ + baseResource.addAll(classpath); + } + for (URL url : baseResource) { + String path = url.getPath(); + if(path.contains(PROD_CLASSES_URL_SIGN)){ + classLoader.addResource(new MainJarResourceLoader(url)); + } else { + classLoader.addResource(new JarResourceLoader(url)); + } + } + } + + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationJarOuterLauncher.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationJarOuterLauncher.java new file mode 100755 index 0000000..8b5553f --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/IsolationJarOuterLauncher.java @@ -0,0 +1,80 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.isolation; + +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.JarResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.MainJarResourceLoader; +import com.gitee.starblues.loader.launcher.classpath.ClasspathResource; +import com.gitee.starblues.loader.launcher.classpath.JarOutClasspathResource; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; + +import java.io.File; +import java.net.URL; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static com.gitee.starblues.loader.LoaderConstant.PROD_CLASSES_URL_SIGN; + + +/** + * 主程序jar-outer 模式启动者 + * + * @author starBlues + * @since 3.0.2 + * @version 3.1.2 + */ +public class IsolationJarOuterLauncher extends IsolationBaseLauncher { + + private final ClasspathResource classpathResource; + + public IsolationJarOuterLauncher(MethodRunner methodRunner, File rootJarFile) { + super(methodRunner); + Objects.requireNonNull(rootJarFile, "参数 rootJarFile 不能为空"); + this.classpathResource = new JarOutClasspathResource(rootJarFile); + } + + @Override + protected ClassLoader createClassLoader(String... args) throws Exception { + GenericClassLoader classLoader = (GenericClassLoader) super.createClassLoader(args); + addResource(classLoader); + return classLoader; + } + + @Override + protected boolean resolveThreadClassLoader() { + return true; + } + + private void addResource(GenericClassLoader classLoader) throws Exception { + Set baseResource = getBaseResource(); + List classpath = classpathResource.getClasspath(); + if(classpath != null){ + baseResource.addAll(classpath); + } + for (URL url : baseResource) { + String path = url.getPath(); + if(path.contains(PROD_CLASSES_URL_SIGN)){ + classLoader.addResource(new MainJarResourceLoader(url)); + } else { + classLoader.addResource(url); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/ResourceLoaderFactoryGetter.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/ResourceLoaderFactoryGetter.java new file mode 100755 index 0000000..c69f16c --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/isolation/ResourceLoaderFactoryGetter.java @@ -0,0 +1,89 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.isolation; + +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.storage.*; + +import java.util.Objects; + +/** + * 获取ResourceLoaderFactory + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class ResourceLoaderFactoryGetter { + + private static final String PARAMS_KEY = "--resource.store.mode"; + + + /** + * 资源存储模式-永久缓存。速度最快, 但启动前后占用都比较内存高 + */ + private static final String RESOURCE_MODE_PERPETUAL = "perpetual"; + + /** + * 资源存储模式-快速模式且内存相对较低。启动占用内存稍高, 速度比较高, 启动完成后占用内存会降低 + */ + private static final String RESOURCE_MODE_FAST_LOW = "fast-low"; + + + /** + * 资源存储模式--缓存可释放模式 + */ + private static final String RESOURCE_MODE_CACHE_RELEASED = "cache-released"; + + + private static volatile String resourceMode; + + + static ResourceLoaderFactory get(String classLoaderName, String... args){ + if(resourceMode == null){ + synchronized (ResourceLoaderFactory.class){ + if(resourceMode == null){ + resourceMode = parseArg(args); + } + } + } + return new DefaultResourceLoaderFactory(classLoaderName); + } + + private static String parseArg(String... args){ + for (String arg : args) { + if(arg.startsWith(PARAMS_KEY)){ + String[] split = arg.split("="); + if(split.length != 2){ + return null; + } + return split[1]; + } + } + return null; + } + + public static AbstractResourceStorage getResourceStorage(String key){ + if(Objects.equals(resourceMode, RESOURCE_MODE_PERPETUAL)){ + return new CachePerpetualResourceStorage(); + } else { + return new CacheFastResourceStorage(key); + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java new file mode 100755 index 0000000..370df25 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MainMethodRunner.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.runner; + +/** + * 主程序方法启动者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class MainMethodRunner extends MethodRunner{ + + public MainMethodRunner(String mainClass, String mainRunMethod, String[] args) { + super(mainClass, mainRunMethod, args); + } + + @Override + protected Object getInstance(Class mainClass) throws Exception { + return null; + } +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java new file mode 100755 index 0000000..502e012 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/launcher/runner/MethodRunner.java @@ -0,0 +1,116 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.launcher.runner; + +import com.gitee.starblues.loader.utils.CompareClassTypeUtils; +import com.gitee.starblues.loader.utils.ObjectUtils; + +import java.lang.reflect.Method; +import java.util.Objects; + +/** + * 反射运行方法 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class MethodRunner { + + protected final String className; + protected final String runMethodName; + + protected String[] args; + + public MethodRunner(String className, String runMethodName, String[] args) { + this.className = checkEmpty(className, "className 不能为空"); + this.runMethodName = checkEmpty(runMethodName, "runMethod 不能为空"); + this.args = (args != null) ? args.clone() : null; + } + + public Object run() throws Exception { + return run(null); + } + + public Object run(ClassLoader classLoader) throws Exception { + Class runClass = loadRunClass(classLoader); + return runMethod(runClass); + } + + protected Class loadRunClass(ClassLoader classLoader) throws Exception{ + if(classLoader == null){ + classLoader = Thread.currentThread().getContextClassLoader(); + } + return Class.forName(this.className, false, classLoader); + } + + protected Object runMethod(Class runClass) throws Exception { + Method runMethod = findRunMethod(runClass); + if(runMethod == null) { + throw new NoSuchMethodException(runClass.getName() + "." + runMethodName + "(String[] args)"); + } + Object instance = getInstance(runClass); + runMethod.setAccessible(true); + runMethod.invoke(instance, new Object[] { this.args }); + return instance; + } + + protected Object getInstance(Class runClass) throws Exception { + return runClass.getConstructor().newInstance(); + } + + private String checkEmpty(String value, String msg){ + if(ObjectUtils.isEmpty(value)){ + throw new IllegalArgumentException(msg); + } + return value; + } + + private Method findRunMethod(Class runClass){ + Class searchType = runClass; + Class[] argClasses = new Class[]{ String[].class }; + while (searchType != null) { + Method[] methods = searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods(); + for (Method method : methods) { + if(!Objects.equals(method.getName(), runMethodName)){ + continue; + } + if(hasSameParams(method, argClasses)){ + return method; + } + } + searchType = searchType.getSuperclass(); + } + return null; + } + + private boolean hasSameParams(Method method, Class[] paramTypes) { + if(paramTypes.length != method.getParameterCount()){ + return false; + } + Class[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < paramTypes.length; i++) { + Class paramType = paramTypes[i]; + Class methodParamType = parameterTypes[i]; + if(CompareClassTypeUtils.compare(methodParamType, paramType)){ + return true; + } + } + return false; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java new file mode 100755 index 0000000..26aa30b --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Assert.java @@ -0,0 +1,89 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.utils; + + +import java.util.function.Supplier; + +/** + * 参数校验工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class Assert { + + private Assert(){}; + + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } + + public static void isTrue(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + } + + public static void state(boolean expression, String message) { + if (!expression) { + throw new IllegalStateException(message); + } + } + + public static void state(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalStateException(nullSafeGet(messageSupplier)); + } + } + + public static T isNotNull(T t, String message) { + if (t == null) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotNull(T t, Supplier messageSupplier) { + if (t == null) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + public static T isNotEmpty(T t, String message) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(message); + } + return t; + } + + public static T isNotEmpty(T t, Supplier messageSupplier) { + if (ObjectUtils.isEmpty(t)) { + throw new IllegalArgumentException(nullSafeGet(messageSupplier)); + } + return t; + } + + private static String nullSafeGet(Supplier messageSupplier) { + return (messageSupplier != null ? messageSupplier.get() : null); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java new file mode 100755 index 0000000..c1ea6e9 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/CompareClassTypeUtils.java @@ -0,0 +1,101 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.utils; + +/** + * 比较两个类类型 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class CompareClassTypeUtils { + + private CompareClassTypeUtils(){} + + public static boolean compare(Class class1, Class class2){ + if(class1.isAssignableFrom(class2)){ + return true; + } + if(isBoolean(class1) && isBoolean(class2)){ + return true; + } + if(isChar(class1) && isChar(class2)){ + return true; + } + if(isByte(class1) && isByte(class2)){ + return true; + } + if(isShort(class1) && isShort(class2)){ + return true; + } + if(isInt(class1) && isInt(class2)){ + return true; + } + if(isLong(class1) && isLong(class2)){ + return true; + } + if(isFloat(class1) && isFloat(class2)){ + return true; + } + if(isDouble(class1) && isDouble(class2)){ + return true; + } + if(isVoid(class1) && isVoid(class2)){ + return true; + } + return false; + } + + + public static boolean isBoolean(Class class1){ + return class1.isAssignableFrom(Boolean.class) || class1.isAssignableFrom(boolean.class); + } + + public static boolean isChar(Class class1){ + return class1.isAssignableFrom(Character.class) || class1.isAssignableFrom(char.class); + } + + public static boolean isByte(Class class1){ + return class1.isAssignableFrom(Byte.class) || class1.isAssignableFrom(byte.class); + } + + public static boolean isShort(Class class1){ + return class1.isAssignableFrom(Short.class) || class1.isAssignableFrom(short.class); + } + + public static boolean isInt(Class class1){ + return class1.isAssignableFrom(Integer.class) || class1.isAssignableFrom(int.class); + } + + public static boolean isLong(Class class1){ + return class1.isAssignableFrom(Long.class) || class1.isAssignableFrom(long.class); + } + + public static boolean isFloat(Class class1){ + return class1.isAssignableFrom(Float.class) || class1.isAssignableFrom(float.class); + } + + public static boolean isDouble(Class class1){ + return class1.isAssignableFrom(Double.class) || class1.isAssignableFrom(double.class); + } + + public static boolean isVoid(Class class1){ + return class1.isAssignableFrom(Void.class) || class1.isAssignableFrom(void.class); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/FilesUtils.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/FilesUtils.java new file mode 100755 index 0000000..ada15d7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/FilesUtils.java @@ -0,0 +1,128 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.utils; + +import com.gitee.starblues.loader.LoaderConstant; + +import java.io.File; +import java.io.IOException; + +/** + * 文件工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public class FilesUtils { + + /** + * 获取存在的文件 + * + * @param pathStr 文件路径 + * @return File + */ + public static File getExistFile(String pathStr){ + File file = new File(pathStr); + if(file.exists()){ + return file; + } + return null; + } + + + /** + * 拼接file路径 + * + * @param paths 拼接的路径 + * @return 拼接的路径 + */ + public static String joiningFilePath(String ...paths){ + if(paths == null || paths.length == 0){ + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + int length = paths.length; + for (int i = 0; i < length; i++) { + String path = paths[i]; + if(ObjectUtils.isEmpty(path)) { + continue; + } + if(i > 0){ + if(path.startsWith(File.separator) || path.startsWith("/") || + path.startsWith("\\") || path.startsWith("//")){ + stringBuilder.append(path); + } else { + stringBuilder.append(File.separator).append(path); + } + } else { + stringBuilder.append(path); + } + } + + return stringBuilder.toString(); + } + + public static File createFile(String path) throws IOException { + try { + File file = new File(path); + File parentFile = file.getParentFile(); + if(!parentFile.exists()){ + if(!parentFile.mkdirs()){ + throw new IOException("Create " + parentFile + " dir error"); + } + } + if(file.createNewFile()){ + return file; + } + throw new IOException("Create " + path + " file error"); + } catch (Exception e){ + throw new IOException("Create " + path + " file error"); + } + } + + + /** + * 解决相对路径 + * @param rootPath 根路径 + * @param relativePath 以 ~ 开头的相对路径 + * @return 处理后的路径 + */ + public static String resolveRelativePath(String rootPath, String relativePath){ + if(ObjectUtils.isEmpty(relativePath)){ + return relativePath; + } + if(isRelativePath(relativePath)){ + return joiningFilePath(rootPath, relativePath.replaceFirst(LoaderConstant.RELATIVE_SIGN, "")); + } else { + return relativePath; + } + } + + /** + * 是否是相对路径 + * @param path 路径 + * @return true 为相对路径, false 未非相对路径 + */ + public static boolean isRelativePath(String path){ + if(ObjectUtils.isEmpty(path)){ + return false; + } + return path.startsWith(LoaderConstant.RELATIVE_SIGN); + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java new file mode 100755 index 0000000..20cec12 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/IOUtils.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.utils; + +import java.io.*; +import java.util.function.Consumer; + +/** + * io utils + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class IOUtils { + + public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException { + if(inputStream == null){ + throw new IllegalArgumentException("参数inputStream不能为空"); + } + if(outputStream == null){ + throw new IllegalArgumentException("参数inputStream不能为空"); + } + BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream); + byte[] arr = new byte[1024]; + int len ; + while ((len = bufferedInputStream.read(arr)) != -1) { + bufferedOutputStream.write(arr, 0, len); + } + bufferedOutputStream.flush(); + } + + public static byte[] read(InputStream inputStream) throws IOException { + if(inputStream == null){ + throw new IllegalArgumentException("参数inputStream不能为空"); + } + if(!(inputStream instanceof BufferedInputStream)){ + inputStream = new BufferedInputStream(inputStream); + } + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){ + int len = 0; + byte[] buffer = new byte[1024]; + while (-1 != (len = inputStream.read(buffer))) { + outputStream.write(buffer, 0, len); + } + outputStream.flush(); + return outputStream.toByteArray(); + } + } + + public static void closeQuietly(final AutoCloseable closeable) { + closeQuietly(closeable, null); + } + + public static void closeQuietly(final AutoCloseable closeable, final Consumer consumer) { + if (closeable != null) { + try { + closeable.close(); + } catch (final Exception e) { + if (consumer != null) { + consumer.accept(e); + } + } + } + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java new file mode 100755 index 0000000..de58e49 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ObjectUtils.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.utils; + +import java.lang.reflect.Array; +import java.util.*; + +/** + * object utils + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ObjectUtils { + + private ObjectUtils(){} + + public static boolean isEmpty(CharSequence cs) { + return cs == null || cs.length() == 0; + } + + public static boolean isEmpty(Object obj) { + if (obj == null) { + return true; + } + + if (obj instanceof Optional) { + return !((Optional) obj).isPresent(); + } + if (obj instanceof CharSequence) { + return ((CharSequence) obj).length() == 0; + } + if (obj.getClass().isArray()) { + return Array.getLength(obj) == 0; + } + if (obj instanceof Collection) { + return ((Collection) obj).isEmpty(); + } + if (obj instanceof Map) { + return ((Map) obj).isEmpty(); + } + return false; + } + + public static List toList(T... array){ + if(array.length == 0){ + return new ArrayList<>(0); + } + List list = new ArrayList<>(array.length); + for (T t : array) { + list.add(t); + } + return list; + } + + public static T getFirst(Collection collection){ + if(ObjectUtils.isEmpty(collection)){ + return null; + } + for (T t : collection) { + return t; + } + return null; + } + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Release.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Release.java new file mode 100755 index 0000000..8f75fdd --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/Release.java @@ -0,0 +1,18 @@ +package com.gitee.starblues.loader.utils; + +/** + * 可释放资源接口 + * + * @author starBlues + * @since 3.1.1 + * @version 3.1.1 + */ +public interface Release { + + /** + * 释放资源 + * @throws Exception 释放异常 + */ + default void release() throws Exception{} + +} diff --git a/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java new file mode 100755 index 0000000..0de81f7 --- /dev/null +++ b/iot-spring-brick/spring-brick-loader/src/main/java/com/gitee/starblues/loader/utils/ResourceUtils.java @@ -0,0 +1,229 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.loader.utils; + + +import java.io.File; +import java.io.InputStream; +import java.net.*; +import java.util.Collection; +import java.util.function.Consumer; + +/** + * 资源工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class ResourceUtils { + + public static final String URL_PROTOCOL_FILE = "file"; + public static final String URL_PROTOCOL_JAR_FILE = "jar"; + public static final String JAR_FILE_EXTENSION = ".jar"; + public static final String ZIP_FILE_EXTENSION = ".zip"; + + public static final String URL_PROTOCOL_VFSFILE = "vfsfile"; + public static final String URL_PROTOCOL_VFS = "vfs"; + + public static final String PACKAGE_SPLIT = "/"; + + public static final String CLASS_FILE_EXTENSION = ".class"; + + private ResourceUtils(){} + + /** + * 是否为jar文件 + * @param url url + * @return boolean + */ + public static boolean isJarFileUrl(URL url) { + String protocol = url.getProtocol(); + boolean extensionIsJar = url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION); + return (URL_PROTOCOL_FILE.equals(protocol) && extensionIsJar) + || (URL_PROTOCOL_JAR_FILE.equals(protocol) || extensionIsJar); + } + + /** + * 是否为jar文件 + * @param file file + * @return boolean + */ + public static boolean isJarFile(File file) { + return file.getName().toLowerCase().endsWith(JAR_FILE_EXTENSION); + } + + /** + * 是否为zip文件 + * @param url url + * @return boolean + */ + public static boolean isZipFileUrl(URL url) { + String protocol = url.getProtocol(); + boolean extensionIsZip = url.getPath().toLowerCase().endsWith(ZIP_FILE_EXTENSION); + return (URL_PROTOCOL_FILE.equals(protocol) && extensionIsZip); + } + + /** + * 是否为jar协议的文件 + * @param url url + * @return boolean + */ + public static boolean isJarProtocolUrl(URL url) { + return URL_PROTOCOL_JAR_FILE.equals(url.getProtocol()); + } + + + /** + * 是否为普通文件 + * @param url url + * @return boolean + */ + public static boolean isFileUrl(URL url) { + String protocol = url.getProtocol(); + return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) || + URL_PROTOCOL_VFS.equals(protocol)); + } + + /** + * 将资源名称统一格式化为标准格式 + * 标准格式为 a/b/c + * @param name 原始资源名称 + * @return 标准资源名称 + */ + public static String formatStandardName(String name){ + if(ObjectUtils.isEmpty(name)) { + return PACKAGE_SPLIT; + } + String[] split = name.split(PACKAGE_SPLIT); + StringBuilder newPath = null; + for (String s : split) { + if ("".equals(s)) { + continue; + } + if (newPath == null) { + newPath = new StringBuilder(s); + } else { + newPath.append(PACKAGE_SPLIT).append(s); + } + } + if(newPath == null || newPath.length() == 0){ + return PACKAGE_SPLIT; + } + return newPath.toString(); + } + + /** + * 释放资源 + * @param object 释放资源的对象 + */ + public static void release(final Object object) { + release(object, null); + } + + /** + * 释放资源 + * @param object 释放资源的对象 + * @param consumer 释放异常消费 + */ + public static void release(final Object object, final Consumer consumer) { + if (object instanceof Release) { + release((Release) object, consumer); + } + if(object instanceof Collection){ + Collection collection = (Collection) object; + for (Object o : collection) { + if(o instanceof Release){ + release((Release) o, consumer); + } + } + } + } + + /** + * 释放资源 + * @param release release + * @param consumer consumer + */ + public static void release(final Release release, final Consumer consumer) { + if(release == null){ + return; + } + try { + release.release(); + } catch (final Exception e) { + if (consumer != null) { + consumer.accept(e); + } + } + } + + /** + * 获取存在的url + * @param url url + * @return 存在的URL, 不存在返回nulll + */ + public static URL getExistUrl(URL url){ + return getExistUrl(url, null); + } + + /** + * 获取存在的url + * @param baseUrl 根url + * @param name 资源名称 + * @return 存在的URL, 不存在返回nulll + */ + public static URL getExistUrl(URL baseUrl, String name){ + URL url; + try { + if(ObjectUtils.isEmpty(name)){ + url = baseUrl; + } else { + url = new URL(baseUrl, name); + } + } catch (MalformedURLException e) { + throw new IllegalArgumentException("非法:" + name); + } + URLConnection uc = null; + try { + uc = url.openConnection(); + if (uc instanceof HttpURLConnection) { + HttpURLConnection hconn = (HttpURLConnection)uc; + hconn.setRequestMethod("HEAD"); + if (hconn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) { + hconn.getInputStream().close(); + return null; + } + } else { + uc.setUseCaches(false); + InputStream is = uc.getInputStream(); + is.close(); + } + return url; + } catch (Exception e) { + return null; + } + } + + public static boolean isClass(String path){ + if(ObjectUtils.isEmpty(path)){ + return false; + } + return path.toLowerCase().endsWith(CLASS_FILE_EXTENSION); + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/pom.xml b/iot-spring-brick/spring-brick-maven-packager/pom.xml new file mode 100755 index 0000000..e6768e1 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + + com.gitee.starblues + iot-spring-brick + 3.1.4 + + + spring-brick-maven-packager + jar + + 用于打包主程序/插件模块 + + + 3.0.0 + ${java.version} + ${java.version} + UTF-8 + UTF-8 + UTF-8 + + 3.8.4 + 3.6.2 + 3.2.0 + 3.8.1 + 1.21 + + 3.6.0 + + + + + + com.gitee.starblues + spring-brick-common + ${project.version} + + + + org.apache.maven + maven-plugin-api + ${maven-plugin-api.version} + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-annotations.version} + + + + org.apache.maven.shared + maven-common-artifact-filters + ${maven-common-artifact-filters.version} + + + + org.apache.commons + commons-compress + ${commons-compress.version} + + + + commons-io + commons-io + + + + junit + junit + ${junit.version} + test + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven-plugin-plugin.version} + + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java new file mode 100755 index 0000000..ff061cf --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractDependencyFilterMojo.java @@ -0,0 +1,108 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.plugin.pack.filter.Exclude; +import com.gitee.starblues.plugin.pack.filter.ExcludeFilter; +import com.gitee.starblues.plugin.pack.filter.Include; +import com.gitee.starblues.plugin.pack.filter.IncludeFilter; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; +import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; +import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 抽象可过滤依赖的 mojo + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public abstract class AbstractDependencyFilterMojo extends AbstractMojo { + + + @Parameter(property = "spring-brick-packager.includes") + private List includes; + + @Parameter(property = "spring-brick-packager.excludes") + private List excludes; + + + protected final Set filterDependencies(Set dependencies, FilterArtifacts filters) + throws MojoExecutionException { + try { + Set filtered = new LinkedHashSet<>(dependencies); + filtered.retainAll(filters.filter(dependencies)); + return filtered; + } + catch (ArtifactFilterException ex) { + throw new MojoExecutionException(ex.getMessage(), ex); + } + } + + protected final FilterArtifacts getFilters(ArtifactsFilter... additionalFilters) { + FilterArtifacts filters = new FilterArtifacts(); + for (ArtifactsFilter additionalFilter : additionalFilters) { + filters.addFilter(additionalFilter); + } + if (!ObjectUtils.isEmpty(includes)) { + filters.addFilter(new IncludeFilter(this.includes)); + } + if(ObjectUtils.isEmpty(excludes)){ + excludes = new ArrayList<>(); + } + // 添加主框架排除 + addPluginFrameworkExclude(); + // 添加spring web 环境排除 + addSpringWebEnvExclude(); + filters.addFilter(new ExcludeFilter(this.excludes)); + return filters; + } + + private void addPluginFrameworkExclude(){ + excludes.add(CommonUtils.getPluginFrameworkExclude()); + } + + private void addSpringWebEnvExclude(){ + excludes.add(Exclude.get("org.springframework.boot", "spring-boot-starter-web")); + excludes.add(Exclude.get("org.springframework.boot", "spring-boot-starter-tomcat")); + excludes.add(Exclude.get("org.springframework.boot", "spring-boot-starter-json")); + excludes.add(Exclude.get("org.springframework", "spring-webmvc")); + excludes.add(Exclude.get("org.springframework", "spring-web")); + + // jackson + excludes.add(Exclude.get("com.fasterxml.jackson.core", "jackson-core")); + excludes.add(Exclude.get("com.fasterxml.jackson.core", "jackson-databind")); + excludes.add(Exclude.get("com.fasterxml.jackson.core", "jackson-annotations")); + excludes.add(Exclude.get("com.fasterxml.jackson.module", "jackson-module-parameter-names")); + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java new file mode 100755 index 0000000..629649d --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/AbstractPackagerMojo.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.util.Set; + +/** + * 抽象的重新打包 mojo + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo{ + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + @Parameter(defaultValue = "${project.build.directory}", required = true) + private File outputDirectory; + + @Parameter(property = "spring-brick-packager.mode", defaultValue = "dev", required = true) + private String mode; + + @Parameter(property = "spring-brick-packager.skip", defaultValue = "false") + private boolean skip; + + @Parameter + private String classifier; + + @Parameter(property = "spring-brick-packager.pluginInfo") + private PluginInfo pluginInfo; + + @Parameter(property = "spring-brick-packager.loadMainResourcePattern", required = false) + private LoadMainResourcePattern loadMainResourcePattern; + + @Parameter(property = "spring-brick-packager.includeSystemScope", defaultValue = "true", required = false) + private Boolean includeSystemScope; + + @Override + public final void execute() throws MojoExecutionException, MojoFailureException { + if(Constant.isPom(this.getProject().getPackaging())){ + getLog().debug("repackage goal could not be applied to pom project."); + return; + } + if (this.skip) { + getLog().debug("skipping plugin package."); + return; + } + pack(); + } + + /** + * 打包 + * @throws MojoExecutionException MojoExecutionException + * @throws MojoFailureException MojoFailureException + */ + protected abstract void pack() throws MojoExecutionException, MojoFailureException; + + public final Set getFilterDependencies() throws MojoExecutionException { + Set artifacts = project.getArtifacts(); + return filterDependencies(artifacts, getFilters()); + } + + public final Set getSourceDependencies() throws MojoExecutionException { + return project.getArtifacts(); + } +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java new file mode 100755 index 0000000..bfe59b6 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/BasicRepackager.java @@ -0,0 +1,380 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.common.*; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; +import static com.gitee.starblues.common.PluginDescriptorKey.*; + +/** + * 基础打包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class BasicRepackager implements Repackager{ + + @Getter + private String rootDir; + private String relativeManifestPath; + private String relativePluginMetaPath; + private String relativeResourcesDefinePath; + + protected File resourcesDefineFile; + + protected final RepackageMojo repackageMojo; + protected JarFile sourceJarFile; + + public BasicRepackager(RepackageMojo repackageMojo) { + this.repackageMojo = repackageMojo; + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + sourceJarFile = CommonUtils.getSourceJarFile(repackageMojo.getProject()); + checkPluginInfo(); + rootDir = createRootDir(); + relativeManifestPath = getRelativeManifestPath(); + relativePluginMetaPath = getRelativePluginMetaPath(); + relativeResourcesDefinePath = getRelativeResourcesDefinePath(); + try { + Manifest manifest = getManifest(); + writeManifest(manifest); + } catch (Exception e) { + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } finally { + IOUtils.closeQuietly(sourceJarFile); + } + } + + private void checkPluginInfo() throws MojoExecutionException { + PluginInfo pluginInfo = repackageMojo.getPluginInfo(); + if(pluginInfo == null){ + throw new MojoExecutionException("configuration.pluginInfo config cannot be empty"); + } + if(ObjectUtils.isEmpty(pluginInfo.getId())){ + throw new MojoExecutionException("configuration.pluginInfo.id config cannot be empty"); + } else { + String id = pluginInfo.getId(); + String illegal = PackageStructure.getIllegal(id); + if(illegal != null){ + throw new MojoExecutionException("configuration.pluginInfo.id config can't contain: " + illegal); + } + } + if(ObjectUtils.isEmpty(pluginInfo.getBootstrapClass())){ + throw new MojoExecutionException("configuration.pluginInfo.bootstrapClass config cannot be empty"); + } + if(ObjectUtils.isEmpty(pluginInfo.getVersion())){ + throw new MojoExecutionException("configuration.pluginInfo.version config cannot be empty"); + } else { + String version = pluginInfo.getVersion(); + String illegal = PackageStructure.getIllegal(version); + if(illegal != null){ + throw new MojoExecutionException("configuration.pluginInfo.version config can't contain: " + illegal); + } + } + } + + protected String getRelativeManifestPath(){ + return MANIFEST; + } + + protected String getRelativeResourcesDefinePath(){ + return RESOURCES_DEFINE_NAME; + } + + protected String getRelativePluginMetaPath(){ + return PLUGIN_META_NAME; + } + + protected String createRootDir() throws MojoFailureException { + String rootDirPath = getBasicRootDir(); + File rootDir = new File(rootDirPath); + CommonUtils.deleteFile(rootDir); + if(rootDir.mkdir()){ + return rootDirPath; + } + throw new MojoFailureException("Failed to create the plugin root directory. " + rootDirPath); + } + + protected String getBasicRootDir(){ + File outputDirectory = repackageMojo.getOutputDirectory(); + return FilesUtils.joiningFilePath(outputDirectory.getPath(), PackageStructure.META_INF_NAME); + } + + protected void writeManifest(Manifest manifest) throws Exception { + String manifestPath = FilesUtils.joiningFilePath(rootDir, resolvePath(this.relativeManifestPath)); + File file = new File(manifestPath); + FileOutputStream outputStream = null; + try { + FileUtils.forceMkdirParent(file); + if(file.createNewFile()){ + outputStream = new FileOutputStream(file, false); + manifest.write(outputStream); + } + } finally { + if(outputStream != null){ + try { + outputStream.close(); + } catch (IOException e) { + repackageMojo.getLog().error(e.getMessage(), e); + } + } + } + } + + protected Manifest getManifest() throws Exception{ + Manifest manifest = null; + if(sourceJarFile != null){ + manifest = sourceJarFile.getManifest(); + } else { + manifest = new Manifest(); + } + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.MANIFEST_VERSION, ManifestKey.MANIFEST_VERSION_1_0); + attributes.putValue(ManifestKey.BUILD_TIME, CommonUtils.getDateTime()); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, getPluginMetaInfoPath()); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_DEV); + // 增加jar包title和version属性 + MavenProject mavenProject = this.repackageMojo.getProject(); + attributes.putValue(ManifestKey.IMPLEMENTATION_TITLE, mavenProject.getArtifactId()); + attributes.putValue(ManifestKey.IMPLEMENTATION_VERSION, mavenProject.getVersion()); + return manifest; + } + + /** + * 得到插件信息存储文件路径 + * @return 插件信息存储文件路径 + * @throws Exception Exception + */ + protected String getPluginMetaInfoPath() throws Exception { + Properties pluginMetaInfo = createPluginMetaInfo(); + return writePluginMetaInfo(pluginMetaInfo); + } + + /** + * 创建插件信息 + * @return Properties + * @throws Exception Exception + */ + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = new Properties(); + PluginInfo pluginInfo = repackageMojo.getPluginInfo(); + properties.put(PLUGIN_ID, pluginInfo.getId()); + properties.put(PLUGIN_BOOTSTRAP_CLASS, pluginInfo.getBootstrapClass()); + properties.put(PLUGIN_VERSION, pluginInfo.getVersion()); + properties.put(PLUGIN_PATH, getPluginPath()); + + String resourcesDefineFilePath = writeResourcesDefineFile(getResourcesDefineContent()); + if(!ObjectUtils.isEmpty(resourcesDefineFilePath)){ + properties.put(PLUGIN_RESOURCES_CONFIG, resourcesDefineFilePath); + } + String configFileName = pluginInfo.getConfigFileName(); + if(!ObjectUtils.isEmpty(configFileName)){ + properties.put(PLUGIN_CONFIG_FILE_NAME, configFileName); + } + String configFileLocation = pluginInfo.getConfigFileLocation(); + if(!ObjectUtils.isEmpty(configFileLocation)){ + properties.put(PLUGIN_CONFIG_FILE_LOCATION, configFileLocation); + } + String args = pluginInfo.getArgs(); + if(!ObjectUtils.isEmpty(args)){ + properties.put(PLUGIN_ARGS, args); + } + String provider = pluginInfo.getProvider(); + if(!ObjectUtils.isEmpty(provider)){ + properties.put(PLUGIN_PROVIDER, provider); + } + String requires = pluginInfo.getRequires(); + if(!ObjectUtils.isEmpty(requires)){ + properties.put(PLUGIN_REQUIRES, requires); + } + String dependencyPlugins = getDependencyPlugin(pluginInfo); + if(!ObjectUtils.isEmpty(dependencyPlugins)){ + properties.put(PLUGIN_DEPENDENCIES, dependencyPlugins); + } + String description = pluginInfo.getDescription(); + if(!ObjectUtils.isEmpty(description)){ + properties.put(PLUGIN_DESCRIPTION, description); + } + String license = pluginInfo.getLicense(); + if(!ObjectUtils.isEmpty(license)){ + properties.put(PLUGIN_LICENSE, license); + } + return properties; + } + + protected String getDependencyPlugin(PluginInfo pluginInfo){ + List dependencyPlugins = pluginInfo.getDependencyPlugins(); + return AbstractDependencyPlugin.toStr(dependencyPlugins); + } + + /** + * 写入插件信息 + * @param properties properties + * @return String + * @throws IOException IOException + */ + protected String writePluginMetaInfo(Properties properties) throws Exception { + File pluginMetaFile = createPluginMetaFile(); + try (OutputStreamWriter writer = new OutputStreamWriter( + Files.newOutputStream(pluginMetaFile.toPath()), StandardCharsets.UTF_8)){ + properties.store(writer, Constant.PLUGIN_METE_COMMENTS); + return pluginMetaFile.getPath(); + } + } + + /** + * 创建插件信息存储文件 + * @return File + * @throws IOException 创建文件异常 + */ + protected File createPluginMetaFile() throws IOException { + String path = FilesUtils.joiningFilePath(rootDir, resolvePath(relativePluginMetaPath)); + return FilesUtils.createFile(path); + } + + /** + * 获取插件路径 + * @return 插件路径 + */ + protected String getPluginPath(){ + return repackageMojo.getProject().getBuild().getOutputDirectory(); + } + + protected String writeResourcesDefineFile(String resourcesDefineContent) throws Exception{ + resourcesDefineFile = createResourcesDefineFile(); + FileUtils.write(resourcesDefineFile, resourcesDefineContent, CHARSET_NAME, true); + return resourcesDefineFile.getPath(); + } + + protected File createResourcesDefineFile() throws IOException { + String path = FilesUtils.joiningFilePath(rootDir, resolvePath(relativeResourcesDefinePath)); + return FilesUtils.createFile(path); + } + + protected String getResourcesDefineContent() throws Exception { + String dependenciesIndex = getDependenciesIndex(); + String loadMainResources = getLoadMainResources(); + boolean indexIsEmpty = ObjectUtils.isEmpty(dependenciesIndex); + boolean resourceIsEmpty = ObjectUtils.isEmpty(loadMainResources); + + if(!indexIsEmpty && !resourceIsEmpty){ + return dependenciesIndex + "\n" + loadMainResources; + } else if(!indexIsEmpty){ + return dependenciesIndex; + } else if(!resourceIsEmpty){ + return loadMainResources; + } else { + return ""; + } + } + + protected String getDependenciesIndex() throws Exception { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(RESOURCES_DEFINE_DEPENDENCIES).append("\n"); + Set libIndex = getDependenciesIndexSet(); + for (String index : libIndex) { + stringBuilder.append(index).append("\n"); + } + return stringBuilder.toString(); + } + + protected String getLoadMainResources(){ + LoadMainResourcePattern loadMainResourcePattern = repackageMojo.getLoadMainResourcePattern(); + if(loadMainResourcePattern == null){ + return null; + } + String[] includes = loadMainResourcePattern.getIncludes(); + String[] excludes = loadMainResourcePattern.getExcludes(); + StringBuilder stringBuilder = new StringBuilder(); + addLoadMainResources(stringBuilder, RESOURCES_DEFINE_LOAD_MAIN_INCLUDES, includes); + addLoadMainResources(stringBuilder, RESOURCES_DEFINE_LOAD_MAIN_EXCLUDES, excludes); + return stringBuilder.toString(); + } + + protected Set getDependenciesIndexSet() throws Exception { + Set dependencies = repackageMojo.getFilterDependencies(); + Set libPaths = new LinkedHashSet<>(dependencies.size()); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + libPaths.add(getLibIndex(artifact)); + } + return libPaths; + } + + protected String getLibIndex(Artifact artifact){ + return artifact.getFile().getPath() + repackageMojo.resolveLoadToMain(artifact); + } + + private void addLoadMainResources(StringBuilder stringBuilder, String header, String[] patterns){ + if(ObjectUtils.isEmpty(patterns)){ + return; + } + Set patternSet = new LinkedHashSet<>(Arrays.asList(patterns)); + stringBuilder.append(header).append("\n"); + for (String patternStr : patternSet) { + if(ObjectUtils.isEmpty(patternStr)){ + continue; + } + stringBuilder.append(resolvePattern(patternStr)).append("\n"); + } + } + + protected String resolvePattern(String patternStr){ + return patternStr.replace(".", "/"); + } + + /** + * 过滤Artifact + * @param artifact Artifact + * @return 返回true表示被过滤掉 + */ + protected boolean filterArtifact(Artifact artifact){ + return Constant.filterMainTypeArtifact(artifact) || + Constant.filterArtifact(artifact, repackageMojo.getIncludeSystemScope()); + } + + + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java new file mode 100755 index 0000000..0225ae5 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Constant.java @@ -0,0 +1,85 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import org.apache.maven.artifact.Artifact; + +/** + * 静态类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class Constant { + + public static final String PACKAGING_POM = "pom"; + public static final String SCOPE_PROVIDED = "provided"; + public static final String SCOPE_COMPILE = "compile"; + public static final String SCOPE_SYSTEM = "system"; + public static final String SCOPE_TEST = "test"; + + public static final String MAVEN_POM_TYPE = "pom"; + + public static final String MAVEN_MAIN_TYPE = "main"; + + public static final String MODE_MAIN = "main"; + public static final String MODE_DEV = "dev"; + public static final String MODE_PROD = "prod"; + + public static final String PLUGIN_METE_COMMENTS = "plugin meta configuration"; + + /** + * 开发模式方法名称 + */ + public static final String DEVELOPMENT_MODE_METHOD_NAME = "developmentMode"; + + + public static boolean isPom(String packageType){ + return PACKAGING_POM.equalsIgnoreCase(packageType); + } + + public static boolean filterArtifact(Artifact artifact, Boolean includeSystemScope){ + boolean scopeFilter = Constant.scopeFilter(artifact.getScope()); + if(scopeFilter){ + return true; + } + if(Constant.isSystemScope(artifact.getScope())){ + return includeSystemScope == null || !includeSystemScope; + } + return Constant.filterPomTypeArtifact(artifact); + } + + public static boolean filterMainTypeArtifact(Artifact artifact){ + // 配置了为main的依赖, 则对其过滤 + return MAVEN_MAIN_TYPE.equalsIgnoreCase(artifact.getType()); + } + + public static boolean filterPomTypeArtifact(Artifact artifact){ + return MAVEN_POM_TYPE.equalsIgnoreCase(artifact.getType()); + } + + public static boolean scopeFilter(String scope){ + return SCOPE_PROVIDED.equalsIgnoreCase(scope) + || SCOPE_TEST.equalsIgnoreCase(scope); + } + + public static boolean isSystemScope(String scope){ + return SCOPE_SYSTEM.equalsIgnoreCase(scope); + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java new file mode 100755 index 0000000..7e2b1e0 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Dependency.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 依赖Bean + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class Dependency { + + @Parameter(required = true) + private String groupId; + + @Parameter(required = true) + private String artifactId; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java new file mode 100755 index 0000000..44dfcdf --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/DependencyPlugin.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.common.AbstractDependencyPlugin; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 依赖的插件 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DependencyPlugin extends AbstractDependencyPlugin { + + @Parameter(required = true) + private String id; + + @Parameter(required = true) + private String version; + + @Parameter(required = false, defaultValue = "true") + private Boolean optional = false; + + @Override + public String getId() { + return id; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public Boolean getOptional() { + if(optional == null){ + return false; + } + return optional; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public void setVersion(String version) { + this.version = version; + } + + @Override + public void setOptional(Boolean optional) { + this.optional = optional; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java new file mode 100755 index 0000000..a9a3b7a --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadMainResourcePattern.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 从主程序加载资源配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class LoadMainResourcePattern { + + @Parameter(name = "includes") + private String[] includes; + + @Parameter(name = "excludes") + private String[] excludes; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java new file mode 100755 index 0000000..9892b98 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/LoadToMain.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import lombok.Data; + +import java.util.List; + +/** + * 定义依赖加载到主程序中 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class LoadToMain { + + private List dependencies; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java new file mode 100755 index 0000000..7265f0b --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/PluginInfo.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +import java.util.List; + +/** + * 插件信息 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class PluginInfo { + + /** + * 插件id + */ + @Parameter(required = true) + private String id; + + /** + * 插件引导启动类 + */ + @Parameter(required = true) + private String bootstrapClass; + + /** + * 插件版本 + */ + @Parameter(required = true) + private String version; + + /** + * 插件配置文件名称。 + */ + private String configFileName; + + /** + * 插件配置文件所在目录。如果不填写, 默认从 target/classes 下读取 + */ + private String configFileLocation; + + /** + * 插件启动入口参数配置 + */ + private String args; + + /** + * 插件描述 + */ + private String description; + + /** + * 插件提供者 + */ + private String provider; + + /** + * 需要安装的主程序版本 + */ + private String requires; + + /** + * 插件 license + */ + private String license; + + /** + * 依赖的插件 + */ + private List dependencyPlugins; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java new file mode 100755 index 0000000..cf2f360 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/RepackageMojo.java @@ -0,0 +1,133 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.plugin.pack.dev.DevConfig; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.plugin.pack.encrypt.*; +import com.gitee.starblues.plugin.pack.main.MainConfig; +import com.gitee.starblues.plugin.pack.main.MainRepackager; +import com.gitee.starblues.plugin.pack.prod.ProdConfig; +import com.gitee.starblues.plugin.pack.prod.ProdRepackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 重新打包 mojo + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +@Mojo(name = "repackage", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, + requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) +@Getter +public class RepackageMojo extends AbstractPackagerMojo { + + + @Parameter(property = "spring-brick-packager.devConfig") + private DevConfig devConfig; + + @Parameter(property = "spring-brick-packager.prodConfig") + private ProdConfig prodConfig; + + @Parameter(property = "spring-brick-packager.mainConfig") + private MainConfig mainConfig; + + @Parameter(property = "spring-brick-packager.mainLoad") + private LoadToMain loadToMain; + + @Parameter(property = "spring-brick-packager.encryptConfig") + private EncryptConfig encryptConfig; + + private final Set loadToMainSet = new LinkedHashSet<>(); + + @Override + protected void pack() throws MojoExecutionException, MojoFailureException { + initLoadToMainSet(); + String mode = getMode(); + try { + encrypt(); + } catch (Exception e) { + throw new MojoExecutionException("encrypt failed: " + e.getMessage()); + } + if(Constant.MODE_PROD.equalsIgnoreCase(mode)){ + new ProdRepackager(this).repackage(); + } else if(Constant.MODE_DEV.equalsIgnoreCase(mode)){ + new DevRepackager(this).repackage(); + } else if(Constant.MODE_MAIN.equalsIgnoreCase(mode)){ + new MainRepackager(this).repackage(); + } else { + throw new MojoExecutionException(mode +" model not supported, mode support : " + + Constant.MODE_DEV + "/" + Constant.MODE_PROD); + } + } + + public String resolveLoadToMain(Artifact artifact){ + if(artifact == null){ + return ""; + } + if(loadToMainSet.contains(artifact.getGroupId() + artifact.getArtifactId())){ + return Constants.LOAD_TO_MAIN_SIGN; + } + return ""; + } + + private void initLoadToMainSet(){ + if(loadToMain == null){ + return; + } + List dependencies = loadToMain.getDependencies(); + if(ObjectUtils.isEmpty(dependencies)){ + return; + } + for (Dependency dependency : dependencies) { + loadToMainSet.add(dependency.getGroupId() + dependency.getArtifactId()); + } + } + + /** + * 加密 + * @throws Exception 加密异常 + */ + private void encrypt() throws Exception { + if(encryptConfig == null){ + return; + } + EncryptPlugin encryptPlugin = new EncryptPluginFactory(); + PluginInfo pluginInfo = encryptPlugin.encrypt(encryptConfig, getPluginInfo()); + if(pluginInfo != null){ + setPluginInfo(pluginInfo); + } + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java new file mode 100755 index 0000000..1877f9e --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/Repackager.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +/** + * 重新打包接口 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface Repackager { + + /** + * 重新打包 + * @throws MojoExecutionException MojoExecutionException + * @throws MojoFailureException MojoFailureException + */ + void repackage() throws MojoExecutionException, MojoFailureException; + + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java new file mode 100755 index 0000000..a99bba6 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/Dependency.java @@ -0,0 +1,41 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.dev; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 开发环境下配置本地依赖的Bean + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class Dependency { + + @Parameter(required = true) + private String groupId; + + @Parameter(required = true) + private String artifactId; + + @Parameter(required = true) + private String classesPath; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java new file mode 100755 index 0000000..74d6440 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevConfig.java @@ -0,0 +1,44 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.dev; + +import lombok.Data; + +import java.util.List; + +/** + * 开发模式配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class DevConfig { + + /** + * 当前项目依赖其他模块的定义。 + * 主要定义依赖模块target->classes的目录, 方便开发调试 + */ + private List moduleDependencies; + + /** + * 本地jar依赖文件定义 + */ + private List localJars; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java new file mode 100755 index 0000000..2880f02 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/dev/DevRepackager.java @@ -0,0 +1,102 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.dev; + +import com.gitee.starblues.plugin.pack.BasicRepackager; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; + +import java.util.*; + +/** + * 开发环境打包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DevRepackager extends BasicRepackager { + + @Getter + private Map moduleDependencies = Collections.emptyMap(); + + public DevRepackager(RepackageMojo repackageMojo) { + super(repackageMojo); + } + + @Override + protected Set getDependenciesIndexSet() throws Exception { + DevConfig devConfig = repackageMojo.getDevConfig(); + if(devConfig == null){ + return super.getDependenciesIndexSet(); + } + moduleDependencies = getModuleDependencies(devConfig); + Set dependenciesIndexSet = super.getDependenciesIndexSet(); + for (Dependency dependency : moduleDependencies.values()) { + dependenciesIndexSet.add(dependency.getClassesPath()); + } + List localJars = devConfig.getLocalJars(); + if(!ObjectUtils.isEmpty(localJars)){ + dependenciesIndexSet.addAll(localJars); + } + return dependenciesIndexSet; + } + + @Override + protected boolean filterArtifact(Artifact artifact) { + if(super.filterArtifact(artifact)){ + return true; + } + String moduleDependencyKey = getModuleDependencyKey(artifact.getGroupId(), artifact.getArtifactId()); + Dependency dependency = moduleDependencies.get(moduleDependencyKey); + return dependency != null && !ObjectUtils.isEmpty(dependency.getClassesPath()); + } + + protected Map getModuleDependencies(DevConfig devConfig) { + if(devConfig == null){ + return Collections.emptyMap(); + } + List moduleDependencies = devConfig.getModuleDependencies(); + if(ObjectUtils.isEmpty(moduleDependencies)){ + return Collections.emptyMap(); + } + Map moduleDependenciesMap = new HashMap<>(); + for (Dependency dependency : moduleDependencies) { + String moduleDependencyKey = getModuleDependencyKey(dependency.getGroupId(), + dependency.getArtifactId()); + if(moduleDependencyKey == null){ + continue; + } + moduleDependenciesMap.put(moduleDependencyKey, dependency); + } + return moduleDependenciesMap; + } + + protected String getModuleDependencyKey(String groupId, String artifactId){ + if(ObjectUtils.isEmpty(groupId) || ObjectUtils.isEmpty(artifactId)){ + return null; + } + return groupId + artifactId; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesConfig.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesConfig.java new file mode 100755 index 0000000..a7d4afd --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesConfig.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.encrypt; + +import lombok.Data; + +/** + * aes 加密配置 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +@Data +public class AesConfig { + + private String secretKey; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesEncryptPlugin.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesEncryptPlugin.java new file mode 100755 index 0000000..4b2b0f6 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/AesEncryptPlugin.java @@ -0,0 +1,43 @@ +package com.gitee.starblues.plugin.pack.encrypt; + +import com.gitee.starblues.common.cipher.AbstractPluginCipher; +import com.gitee.starblues.common.cipher.AesPluginCipher; +import com.gitee.starblues.plugin.pack.PluginInfo; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.maven.plugin.MojoExecutionException; + +import java.util.HashMap; +import java.util.Map; + +/** + * rsa 加密者 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public class AesEncryptPlugin implements EncryptPlugin{ + + + @Override + public PluginInfo encrypt(EncryptConfig encryptConfig, PluginInfo pluginInfo) throws Exception{ + AesConfig aesConfig = encryptConfig.getAes(); + if(aesConfig == null){ + return null; + } + + String secretKey = aesConfig.getSecretKey(); + if(ObjectUtils.isEmpty(secretKey)){ + throw new MojoExecutionException("encryptConfig.aes.secretKey can't be empty"); + } + AbstractPluginCipher pluginCipher = new AesPluginCipher(); + Map params = new HashMap<>(); + params.put(AesPluginCipher.SECRET_KEY, secretKey); + pluginCipher.initParams(params); + + String bootstrapClass = pluginInfo.getBootstrapClass(); + String encrypt = pluginCipher.encrypt(bootstrapClass); + pluginInfo.setBootstrapClass(encrypt); + return pluginInfo; + } +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptConfig.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptConfig.java new file mode 100755 index 0000000..769b8db --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptConfig.java @@ -0,0 +1,41 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.encrypt; + +import lombok.Data; + +/** + * 加密配置 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +@Data +public class EncryptConfig { + + /** + * rsa 配置 + */ + private RsaConfig rsa; + + /** + * aes 配置 + */ + private AesConfig aes; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPlugin.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPlugin.java new file mode 100755 index 0000000..5a22fee --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPlugin.java @@ -0,0 +1,40 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.encrypt; + +import com.gitee.starblues.plugin.pack.PluginInfo; + +/** + * 加密插件 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public interface EncryptPlugin { + + + /** + * 加密 + * @param pluginInfo 当前插件信息 + * @param encryptConfig 加密配置 + * @return 加密后得字符 + * @throws Exception 加密异常 + */ + PluginInfo encrypt(EncryptConfig encryptConfig, PluginInfo pluginInfo) throws Exception; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPluginFactory.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPluginFactory.java new file mode 100755 index 0000000..c52148c --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/EncryptPluginFactory.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.encrypt; + +import com.gitee.starblues.plugin.pack.PluginInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * 加密插件工厂 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public class EncryptPluginFactory implements EncryptPlugin { + + private final List encryptPlugins = new ArrayList<>(); + + public EncryptPluginFactory(){ + encryptPlugins.add(new AesEncryptPlugin()); + encryptPlugins.add(new RsaEncryptPlugin()); + } + + @Override + public PluginInfo encrypt(EncryptConfig encryptConfig, PluginInfo pluginInfo) throws Exception{ + for (EncryptPlugin encryptPlugin : encryptPlugins) { + PluginInfo encrypt = encryptPlugin.encrypt(encryptConfig, pluginInfo); + if(encrypt != null){ + return encrypt; + } + } + return pluginInfo; + } +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaConfig.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaConfig.java new file mode 100755 index 0000000..09a8597 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaConfig.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.encrypt; + +import lombok.Data; + +/** + * rsa 加密配置 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +@Data +public class RsaConfig { + + /** + * rsa 公钥 + */ + private String publicKey; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaEncryptPlugin.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaEncryptPlugin.java new file mode 100755 index 0000000..2e1f190 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/encrypt/RsaEncryptPlugin.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.encrypt; + +import com.gitee.starblues.common.cipher.AbstractPluginCipher; +import com.gitee.starblues.common.cipher.RsaPluginCipher; +import com.gitee.starblues.plugin.pack.PluginInfo; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.maven.plugin.MojoExecutionException; + +import java.util.HashMap; +import java.util.Map; + +/** + * rsa 算法插件加密 + * + * @author starBlues + * @since 3.0.1 + * @version 3.0.1 + */ +public class RsaEncryptPlugin implements EncryptPlugin{ + + @Override + public PluginInfo encrypt(EncryptConfig encryptConfig, PluginInfo pluginInfo) throws Exception { + RsaConfig rsaConfig = encryptConfig.getRsa(); + if(rsaConfig == null){ + return null; + } + + String publicKey = rsaConfig.getPublicKey(); + if(ObjectUtils.isEmpty(publicKey)){ + throw new MojoExecutionException("encryptConfig.rsa.publicKey can't be empty"); + } + AbstractPluginCipher pluginCipher = new RsaPluginCipher(); + Map params = new HashMap<>(); + params.put(RsaPluginCipher.PUBLIC_KEY, publicKey); + pluginCipher.initParams(params); + + String bootstrapClass = pluginInfo.getBootstrapClass(); + pluginInfo.setBootstrapClass(pluginCipher.encrypt(bootstrapClass)); + return pluginInfo; + } +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java new file mode 100755 index 0000000..1150a26 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/DependencyFilter.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.filter; + +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter; +import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 依赖过滤 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class DependencyFilter extends AbstractArtifactsFilter { + + private final List filters; + + public DependencyFilter(List dependencies) { + this.filters = dependencies; + } + + @Override + public Set filter(Set artifacts) throws ArtifactFilterException { + if(ObjectUtils.isEmpty(artifacts)){ + return artifacts; + } + Set result = new LinkedHashSet<>(); + for (Artifact artifact : artifacts) { + if (!filter(artifact)) { + result.add(artifact); + } + } + return result; + } + + /** + * 子类过滤结果 + * @param artifact artifact + * @return boolean + */ + protected abstract boolean filter(Artifact artifact); + + protected final boolean equals(Artifact artifact, FilterableDependency dependency) { + if (!dependency.getGroupId().equals(artifact.getGroupId())) { + return false; + } + return dependency.getArtifactId().equals(artifact.getArtifactId()); + } + + protected final List getFilters() { + return this.filters; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java new file mode 100755 index 0000000..0aaff1f --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Exclude.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.filter; + +/** + * 排除的依赖定义 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class Exclude extends FilterableDependency{ + + public static Exclude get(String groupId, String artifactId){ + Exclude exclude = new Exclude(); + exclude.setGroupId(groupId); + exclude.setArtifactId(artifactId); + return exclude; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java new file mode 100755 index 0000000..cb564bf --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/ExcludeFilter.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.filter; + +import org.apache.maven.artifact.Artifact; + +import java.util.Arrays; +import java.util.List; + +/** + * 排除过滤 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ExcludeFilter extends DependencyFilter { + + public ExcludeFilter(Exclude... excludes) { + this(Arrays.asList(excludes)); + } + + public ExcludeFilter(List excludes) { + super(excludes); + } + + @Override + protected boolean filter(Artifact artifact) { + for (FilterableDependency dependency : getFilters()) { + if (equals(artifact, dependency)) { + return true; + } + } + return false; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java new file mode 100755 index 0000000..d51b603 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/FilterableDependency.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.filter; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 可过滤依赖bean + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public abstract class FilterableDependency { + + @Parameter(required = true) + private String groupId; + + @Parameter(required = true) + private String artifactId; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java new file mode 100755 index 0000000..c2bfb7a --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/Include.java @@ -0,0 +1,27 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.filter; + +/** + * 包含的依赖定义 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class Include extends FilterableDependency{ +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java new file mode 100755 index 0000000..c9e8385 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/filter/IncludeFilter.java @@ -0,0 +1,46 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.filter; + +import org.apache.maven.artifact.Artifact; + +import java.util.List; + +/** + * 包含过滤器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class IncludeFilter extends DependencyFilter { + + public IncludeFilter(List includes) { + super(includes); + } + + @Override + protected boolean filter(Artifact artifact) { + for (FilterableDependency dependency : getFilters()) { + if (equals(artifact, dependency)) { + return false; + } + } + return true; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java new file mode 100755 index 0000000..804d778 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarNestPackager.java @@ -0,0 +1,138 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.main; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.Repackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.util.List; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; +import static com.gitee.starblues.common.ManifestKey.*; + +/** + * 嵌套jar打包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class JarNestPackager implements Repackager { + + protected final MainConfig mainConfig; + protected final RepackageMojo repackageMojo; + + protected PackageJar packageJar; + + private JarFile sourceJarFile; + + public JarNestPackager(MainRepackager mainRepackager) { + this.mainConfig = mainRepackager.getMainConfig(); + this.repackageMojo = mainRepackager.getRepackageMojo(); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + try { + sourceJarFile = CommonUtils.getSourceJarFile(repackageMojo.getProject()); + packageJar = new PackageJar(mainConfig.getOutputDirectory(), mainConfig.getFileName()); + writeClasses(); + writeDependencies(); + writeManifest(); + } catch (Exception e) { + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } finally { + IOUtils.closeQuietly(packageJar); + IOUtils.closeQuietly(sourceJarFile); + } + } + + protected void writeManifest() throws Exception { + Manifest manifest = getManifest(); + packageJar.putDirEntry(META_INF_NAME + SEPARATOR); + packageJar.write(PROD_MANIFEST_PATH, manifest::write); + } + + protected Manifest getManifest() throws Exception{ + Manifest manifest = null; + if(sourceJarFile != null){ + manifest = sourceJarFile.getManifest(); + } else { + manifest = new Manifest(); + } + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(MANIFEST_VERSION, MANIFEST_VERSION_1_0); + attributes.putValue(BUILD_TIME, CommonUtils.getDateTime()); + attributes.putValue(START_CLASS, mainConfig.getMainClass()); + attributes.putValue(MAIN_CLASS, MAIN_CLASS_VALUE); + attributes.putValue(MAIN_PACKAGE_TYPE, PackageType.MAIN_PACKAGE_TYPE_JAR); + attributes.putValue(DEVELOPMENT_MODE, mainConfig.getDevelopmentMode()); + + // 增加jar包title和version属性 + MavenProject mavenProject = this.repackageMojo.getProject(); + attributes.putValue(IMPLEMENTATION_TITLE, mavenProject.getArtifactId()); + attributes.putValue(IMPLEMENTATION_VERSION, mavenProject.getVersion()); + return manifest; + } + + protected void writeClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageJar.copyDirToPackage(new File(buildDir), null); + } + + protected void writeDependencies() throws Exception { + Set dependencies = repackageMojo.getSourceDependencies(); + String libDirEntryName = createLibEntry(); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + if(CommonUtils.isPluginFrameworkLoader(artifact)){ + // 本框架loader依赖 + packageJar.copyZipToPackage(artifact.getFile()); + } else { + packageJar.writeDependency(artifact.getFile(), libDirEntryName); + } + } + } + + protected boolean filterArtifact(Artifact artifact) { + return Constant.filterArtifact(artifact, repackageMojo.getIncludeSystemScope()); + } + + protected String createLibEntry() throws Exception { + String libDirEntryName = PROD_LIB_PATH; + packageJar.putDirEntry(libDirEntryName); + return libDirEntryName; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java new file mode 100755 index 0000000..eb9e886 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/JarOuterPackager.java @@ -0,0 +1,146 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.main; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.ManifestKey.*; + +/** + * jar 外置包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public class JarOuterPackager extends JarNestPackager { + + private static final String LIB_INDEXES_SPLIT = " "; + + private final Set dependencyIndexNames = new LinkedHashSet<>(); + + public JarOuterPackager(MainRepackager mainRepackager) { + super(mainRepackager); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + // 生成依赖文件夹 + String rootDir = createRootDir(); + mainConfig.setOutputDirectory(rootDir); + super.repackage(); + } + + @Override + protected void writeClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageJar.copyDirToPackage(new File(buildDir), null); + } + + private String createRootDir() throws MojoFailureException{ + String outputDirectory = mainConfig.getOutputDirectory(); + String fileName = mainConfig.getFileName(); + String rootDirPath = FilesUtils.joiningFilePath(outputDirectory, fileName); + File rootFile = new File(rootDirPath); + CommonUtils.deleteFile(rootFile); + if(rootFile.mkdirs()){ + return rootDirPath; + } else { + throw new MojoFailureException("Create dir failure : " + rootDirPath); + } + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(MANIFEST_VERSION, MANIFEST_VERSION_1_0); + attributes.putValue(START_CLASS, mainConfig.getMainClass()); + attributes.putValue(MAIN_CLASS, MAIN_CLASS_VALUE); + attributes.putValue(MAIN_PACKAGE_TYPE, PackageType.MAIN_PACKAGE_TYPE_JAR_OUTER); + attributes.putValue(MAIN_LIB_DIR, getLibPath()); + attributes.putValue(DEVELOPMENT_MODE, mainConfig.getDevelopmentMode()); + + // 增加jar包title和version属性 + MavenProject mavenProject = this.repackageMojo.getProject(); + attributes.putValue(IMPLEMENTATION_TITLE, mavenProject.getArtifactId()); + attributes.putValue(IMPLEMENTATION_VERSION, mavenProject.getVersion()); + return manifest; + } + + + private String getLibIndexes() throws Exception { + if(dependencyIndexNames.isEmpty()){ + return ""; + } + StringBuilder libName = new StringBuilder(); + for (String dependencyIndexName : dependencyIndexNames) { + libName.append(dependencyIndexName).append(LIB_INDEXES_SPLIT); + } + return libName.toString(); + } + + @Override + protected void writeDependencies() throws Exception { + Set dependencies = repackageMojo.getSourceDependencies(); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + if(CommonUtils.isPluginFrameworkLoader(artifact)){ + // 本框架loader依赖 + packageJar.copyZipToPackage(artifact.getFile()); + } else { + File artifactFile = artifact.getFile(); + String libPath = getLibPath(); + if(FilesUtils.isRelativePath(libPath)){ + libPath = FilesUtils.resolveRelativePath(mainConfig.getOutputDirectory(), getLibPath()); + } else { + libPath = FilesUtils.joiningFilePath(mainConfig.getOutputDirectory(), libPath); + } + String targetFilePath = FilesUtils.joiningFilePath(libPath, artifactFile.getName()); + FileUtils.copyFile(artifactFile, new File(targetFilePath)); + dependencyIndexNames.add(artifactFile.getName()); + } + } + } + + private String getLibPath(){ + String libDir = PackageStructure.LIB_NAME; + if(!ObjectUtils.isEmpty(mainConfig.getLibDir())){ + libDir = mainConfig.getLibDir(); + } + return libDir; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java new file mode 100755 index 0000000..8b9101f --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainConfig.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.main; + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 主程序打包配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@Data +public class MainConfig { + + /** + * 主启动类 + */ + @Parameter(required = true) + private String mainClass; + + /** + * 打包类型。默认:jar + * {@link com.gitee.starblues.common.PackageType#MAIN_PACKAGE_TYPE_JAR} + * {@link com.gitee.starblues.common.PackageType#MAIN_PACKAGE_TYPE_JAR_OUTER} + */ + private String packageType; + + /** + * 文件名称。默认 artifactId-version-repackage + */ + private String fileName; + + /** + * 依赖包所在目录 + */ + private String libDir; + + /** + * 输出文件目录。默认target + */ + private String outputDirectory; + + /** + * 开发模式: + * isolation: 隔离模式[默认] + * coexist: 共享模式 + * simple: 简单模式 + */ + private String developmentMode; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java new file mode 100755 index 0000000..f46ac0c --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/main/MainRepackager.java @@ -0,0 +1,136 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.main; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.Repackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; +import lombok.Getter; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Set; + +/** + * 主程序打包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +@Getter +public class MainRepackager implements Repackager { + + private final RepackageMojo repackageMojo; + private final MainConfig mainConfig; + + public MainRepackager(RepackageMojo repackageMojo) { + this.repackageMojo = repackageMojo; + this.mainConfig = repackageMojo.getMainConfig(); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + checkConfig(); + setDevelopmentMode(); + String packageType = mainConfig.getPackageType(); + Repackager repackager = null; + if(PackageType.MAIN_PACKAGE_TYPE_JAR.equalsIgnoreCase(packageType)){ + repackager = new JarNestPackager(this); + } else if(PackageType.MAIN_PACKAGE_TYPE_JAR_OUTER.equalsIgnoreCase(packageType)){ + repackager = new JarOuterPackager(this); + } else { + throw new MojoFailureException("Not found packageType : " + packageType); + } + repackager.repackage(); + } + + private void checkConfig() throws MojoFailureException { + if(mainConfig == null){ + throw new MojoFailureException("configuration.mainConfig config cannot be empty"); + } + if(ObjectUtils.isEmpty(mainConfig.getMainClass())) { + throw new MojoFailureException("configuration.mainConfig.mainClass config cannot be empty"); + } + String fileName = mainConfig.getFileName(); + if(ObjectUtils.isEmpty(fileName)) { + MavenProject project = repackageMojo.getProject(); + mainConfig.setFileName(project.getArtifactId() + "-" + project.getVersion() + "-repackage"); + } + String packageType = mainConfig.getPackageType(); + if(ObjectUtils.isEmpty(packageType)) { + mainConfig.setPackageType(PackageType.MAIN_PACKAGE_TYPE_JAR); + } + String outputDirectory = mainConfig.getOutputDirectory(); + if(ObjectUtils.isEmpty(outputDirectory)){ + mainConfig.setOutputDirectory(repackageMojo.getOutputDirectory().getPath()); + } + } + + private void setDevelopmentMode() throws MojoFailureException{ + String developmentMode = mainConfig.getDevelopmentMode(); + if(!ObjectUtils.isEmpty(developmentMode)){ + return; + } + try { + File file = new File(repackageMojo.getProject().getBuild().getOutputDirectory()); + Set artifacts = repackageMojo.getProject().getArtifacts(); + + URL[] urls = new URL[artifacts.size() + 1]; + int i = 0; + for (Artifact artifact : artifacts) { + urls[i] = artifact.getFile().toURI().toURL(); + i++; + } + urls[i] = file.toURI().toURL(); + URLClassLoader urlClassLoader = new URLClassLoader(urls, null); + + String mainClass = repackageMojo.getMainConfig().getMainClass(); + if(ObjectUtils.isEmpty(mainClass)){ + throw new Exception("mainConfig.mainClass config can't be empty"); + } + Class aClass = urlClassLoader.loadClass(mainClass); + Method method = ReflectionUtils.findMethod(aClass, Constant.DEVELOPMENT_MODE_METHOD_NAME); + String methodKey = aClass.getName() + "#" + Constant.DEVELOPMENT_MODE_METHOD_NAME + "()"; + if(method == null){ + throw new Exception("Not found method : " + methodKey); + } + method.setAccessible(true); + Object o = aClass.getConstructor().newInstance(); + Object result = method.invoke(o); + if(ObjectUtils.isEmpty(result)){ + throw new Exception(methodKey + " return value can't be empty"); + } + getMainConfig().setDevelopmentMode(String.valueOf(result)); + } catch (Exception e) { + throw new MojoFailureException("Set developmentMode failure:" + e.getMessage()); + } + } + + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java new file mode 100755 index 0000000..8f9118e --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/DirProdRepackager.java @@ -0,0 +1,156 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.*; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * 文件夹包生成 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class DirProdRepackager extends DevRepackager { + + protected final ProdConfig prodConfig; + + + public DirProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo); + this.prodConfig = prodConfig; + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + super.repackage(); + try { + resolveClasses(); + logSuccess(); + } catch (Exception e) { + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } + } + + protected void logSuccess(){ + repackageMojo.getLog().info("Success package prod dir file : " + getRootDir()); + } + + @Override + protected String createRootDir() throws MojoFailureException { + String fileName = prodConfig.getFileName(); + String dirPath = FilesUtils.joiningFilePath(prodConfig.getOutputDirectory(), fileName); + File dirFile = new File(dirPath); + CommonUtils.deleteFile(dirFile); + if(!dirFile.mkdirs()){ + throw new MojoFailureException("Create package dir failure: " + dirFile.getPath()); + } + return dirFile.getPath(); + } + + @Override + protected String getRelativeManifestPath() { + return FilesUtils.joiningFilePath(META_INF_NAME, MANIFEST); + } + + @Override + protected String getRelativePluginMetaPath() { + return FilesUtils.joiningFilePath(META_INF_NAME, PLUGIN_META_NAME); + } + + @Override + protected String getRelativeResourcesDefinePath() { + return FilesUtils.joiningFilePath(META_INF_NAME, RESOURCES_DEFINE_NAME); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, PROD_PLUGIN_META_PATH); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_DIR); + return manifest; + } + + @Override + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = super.createPluginMetaInfo(); + properties.put(PluginDescriptorKey.PLUGIN_PATH, CLASSES_NAME); + properties.put(PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG, PROD_RESOURCES_DEFINE_PATH); + String libDir = prodConfig.getLibDir(); + if(ObjectUtils.isEmpty(libDir)){ + libDir = Constants.RELATIVE_SIGN + PackageStructure.PROD_LIB_PATH; + } + properties.put(PluginDescriptorKey.PLUGIN_LIB_DIR, libDir); + return properties; + } + + protected void resolveClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + String path = FilesUtils.joiningFilePath(getRootDir(), CLASSES_NAME); + File file = new File(path); + FileUtils.forceMkdir(file); + FileUtils.copyDirectory(new File(buildDir), file); + } + + @Override + protected Set getDependenciesIndexSet() throws Exception { + Set dependencies = repackageMojo.getFilterDependencies(); + String libDir = createLibDir(); + Set dependencyIndexNames = new LinkedHashSet<>(dependencies.size()); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + File artifactFile = artifact.getFile(); + FileUtils.copyFile(artifactFile, new File(FilesUtils.joiningFilePath(libDir, artifactFile.getName()))); + dependencyIndexNames.add(artifactFile.getName() + repackageMojo.resolveLoadToMain(artifact)); + } + return dependencyIndexNames; + } + + protected String createLibDir() throws IOException { + String dir = FilesUtils.joiningFilePath(getRootDir(), PackageStructure.LIB_NAME); + File file = new File(dir); + if(file.mkdir()){ + return dir; + } + throw new IOException("Create " + PackageStructure.LIB_NAME + " dir failure"); + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java new file mode 100755 index 0000000..052eaf2 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarNestedProdRepackager.java @@ -0,0 +1,77 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.plugin.pack.utils.PackageZip; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.*; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.PROD_CLASSES_PATH; +import static com.gitee.starblues.common.PackageStructure.PROD_RESOURCES_DEFINE_PATH; + +/** + * jar包生成 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class JarNestedProdRepackager extends ZipProdRepackager { + + + public JarNestedProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo, prodConfig); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + super.repackage(); + } + + protected void logSuccess(){ + repackageMojo.getLog().info("Success package prod jar file : " + + packageZip.getFile().getPath()); + } + + @Override + protected PackageZip getPackageZip() throws Exception { + return new PackageJar(prodConfig.getOutputDirectory(), prodConfig.getFileName()); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + manifest.getMainAttributes().putValue( + ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_JAR); + return manifest; + } + + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java new file mode 100755 index 0000000..4d1eaf0 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/JarOuterProdRepackager.java @@ -0,0 +1,56 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.plugin.pack.utils.PackageZip; + +import java.util.jar.Manifest; + +/** + * jar-outer包生成 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class JarOuterProdRepackager extends ZipOuterProdRepackager { + + + public JarOuterProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo, prodConfig); + } + + @Override + protected PackageZip getPackageZip(String rootDir) throws Exception { + return new PackageJar(rootDir, super.prodConfig.getFileName()); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + manifest.getMainAttributes().putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, + PackageType.PLUGIN_PACKAGE_TYPE_JAR_OUTER); + return manifest; + } + + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java new file mode 100755 index 0000000..10c334b --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdConfig.java @@ -0,0 +1,59 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + + +import lombok.Data; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * 生产环境打包配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +@Data +public class ProdConfig { + + /** + * 打包类型。默认jar包 + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_JAR} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_JAR_OUTER} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_ZIP} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_ZIP_OUTER} + * {@link com.gitee.starblues.common.PackageType#PLUGIN_PACKAGE_TYPE_DIR} + */ + @Parameter(required = true, defaultValue = "jar") + private String packageType = "jar"; + + /** + * 文件名称。默认 pluginId-version-repackage + */ + private String fileName; + + /** + * 输出文件目录。默认target + */ + private String outputDirectory; + + /** + * jar-outer、zip-outer、dir 类型可指定依赖包目录 + */ + private String libDir; + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java new file mode 100755 index 0000000..629d816 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ProdRepackager.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.plugin.pack.PluginInfo; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.Repackager; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.Getter; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + + +/** + * 生产环境打包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ProdRepackager implements Repackager { + + @Getter + private ProdConfig prodConfig; + + private final RepackageMojo repackageMojo; + private final Repackager repackager; + + public ProdRepackager(RepackageMojo repackageMojo) { + this.repackageMojo = repackageMojo; + this.repackager = new DevRepackager(repackageMojo); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + repackager.repackage(); + this.prodConfig = getProdConfig(repackageMojo); + String packageType = prodConfig.getPackageType(); + Repackager repackager = null; + + if(PackageType.PLUGIN_PACKAGE_TYPE_ZIP.equalsIgnoreCase(packageType)){ + repackager = new ZipProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_JAR.equalsIgnoreCase(packageType)){ + repackager = new JarNestedProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER.equalsIgnoreCase(packageType)){ + repackager = new ZipOuterProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_JAR_OUTER.equalsIgnoreCase(packageType)){ + repackager = new JarOuterProdRepackager(repackageMojo, prodConfig); + } else if(PackageType.PLUGIN_PACKAGE_TYPE_DIR.equalsIgnoreCase(packageType)){ + repackager = new DirProdRepackager(repackageMojo, prodConfig); + } else { + throw new MojoFailureException("Not found packageType : " + packageType); + } + repackager.repackage(); + } + + protected ProdConfig getProdConfig(RepackageMojo repackageMojo){ + ProdConfig prodConfig = repackageMojo.getProdConfig(); + if(prodConfig == null){ + prodConfig = new ProdConfig(); + } + if(ObjectUtils.isEmpty(prodConfig.getPackageType())){ + prodConfig.setPackageType(PackageType.PLUGIN_PACKAGE_TYPE_JAR); + } + String fileName = prodConfig.getFileName(); + if(ObjectUtils.isEmpty(fileName)) { + PluginInfo pluginInfo = repackageMojo.getPluginInfo(); + prodConfig.setFileName(pluginInfo.getId() + "-" + pluginInfo.getVersion() + "-repackage"); + } + String outputDirectory = prodConfig.getOutputDirectory(); + if(ObjectUtils.isEmpty(outputDirectory)){ + prodConfig.setOutputDirectory(repackageMojo.getOutputDirectory().getPath()); + } + return prodConfig; + } + + + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java new file mode 100755 index 0000000..87b1f13 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipOuterProdRepackager.java @@ -0,0 +1,128 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.utils.PackageJar; +import com.gitee.starblues.plugin.pack.utils.PackageZip; +import com.gitee.starblues.utils.FilesUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.File; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * zip-outer 包生成 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ZipOuterProdRepackager extends DirProdRepackager { + + protected PackageZip packageZip; + + public ZipOuterProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo, prodConfig); + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + try { + super.repackage(); + } catch (Exception e){ + throw new MojoFailureException(e); + } finally { + if(packageZip != null){ + IOUtils.closeQuietly(packageZip); + } + } + } + + @Override + protected String createRootDir() throws MojoFailureException { + String rootDir = super.createRootDir(); + try { + packageZip = getPackageZip(rootDir); + return rootDir; + } catch (Exception e) { + throw new MojoFailureException(e); + } + } + + protected PackageZip getPackageZip(String rootDir) throws Exception { + return new PackageZip(rootDir, super.prodConfig.getFileName()); + } + + @Override + protected void resolveClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageZip.copyDirToPackage(new File(buildDir), ""); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, PROD_PLUGIN_META_PATH); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER); + return manifest; + } + + @Override + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = super.createPluginMetaInfo(); + properties.put(PluginDescriptorKey.PLUGIN_PATH, packageZip.getFileName()); + return properties; + } + @Override + protected void writeManifest(Manifest manifest) throws Exception { + packageZip.writeManifest(manifest); + } + + @Override + protected String writePluginMetaInfo(Properties properties) throws Exception { + packageZip.write(PROD_PLUGIN_META_PATH, outputStream->{ + properties.store(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), + Constant.PLUGIN_METE_COMMENTS); + }); + return PROD_PLUGIN_META_PATH; + } + + + @Override + protected String writeResourcesDefineFile(String resourcesDefineContent) throws Exception { + packageZip.write(PROD_RESOURCES_DEFINE_PATH, resourcesDefineContent); + return PROD_RESOURCES_DEFINE_PATH; + } +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java new file mode 100755 index 0000000..42ed36f --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/prod/ZipProdRepackager.java @@ -0,0 +1,197 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.prod; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.plugin.pack.Constant; +import com.gitee.starblues.plugin.pack.RepackageMojo; +import com.gitee.starblues.plugin.pack.dev.Dependency; +import com.gitee.starblues.plugin.pack.dev.DevConfig; +import com.gitee.starblues.plugin.pack.dev.DevRepackager; +import com.gitee.starblues.plugin.pack.utils.CommonUtils; +import com.gitee.starblues.plugin.pack.utils.PackageZip; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * zip 打包 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ZipProdRepackager extends DevRepackager { + + + protected final ProdConfig prodConfig; + + protected PackageZip packageZip; + + public ZipProdRepackager(RepackageMojo repackageMojo, ProdConfig prodConfig) { + super(repackageMojo); + this.prodConfig = prodConfig; + } + + @Override + public void repackage() throws MojoExecutionException, MojoFailureException { + try { + packageZip = getPackageZip(); + super.repackage(); + resolveClasses(); + resolveResourcesDefine(); + String rootDir = getRootDir(); + try { + FileUtils.deleteDirectory(new File(rootDir)); + } catch (IOException e) { + // 忽略 + } + logSuccess(); + } catch (Exception e){ + repackageMojo.getLog().error(e.getMessage(), e); + throw new MojoFailureException(e); + } finally { + if(packageZip != null){ + IOUtils.closeQuietly(packageZip); + } + } + } + + protected void logSuccess(){ + repackageMojo.getLog().info("Success package prod zip file : " + + packageZip.getFile().getPath()); + } + + protected PackageZip getPackageZip() throws Exception { + return new PackageZip(prodConfig.getOutputDirectory(), prodConfig.getFileName()); + } + + @Override + protected String getBasicRootDir(){ + File outputDirectory = repackageMojo.getOutputDirectory(); + return FilesUtils.joiningFilePath(outputDirectory.getPath(), UUID.randomUUID().toString()); + } + + @Override + protected Map getModuleDependencies(DevConfig devConfig) { + // 将项目中模块依赖置为空 + return Collections.emptyMap(); + } + + @Override + protected String getPluginPath() { + return CLASSES_NAME + SEPARATOR; + } + + @Override + protected boolean filterArtifact(Artifact artifact) { + return Constant.scopeFilter(artifact.getScope()); + } + + protected void resolveClasses() throws Exception { + String buildDir = repackageMojo.getProject().getBuild().getOutputDirectory(); + packageZip.copyDirToPackage(new File(buildDir), null); + } + + @Override + protected Manifest getManifest() throws Exception { + Manifest manifest = super.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(ManifestKey.PLUGIN_META_PATH, PROD_PLUGIN_META_PATH); + attributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, PackageType.PLUGIN_PACKAGE_TYPE_ZIP); + return manifest; + } + + @Override + protected Properties createPluginMetaInfo() throws Exception { + Properties properties = super.createPluginMetaInfo(); + properties.put(PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG, PROD_RESOURCES_DEFINE_PATH); + properties.put(PluginDescriptorKey.PLUGIN_LIB_DIR, PROD_LIB_PATH); + return properties; + } + + @Override + protected void writeManifest(Manifest manifest) throws Exception { + packageZip.writeManifest(manifest); + } + + @Override + protected String writePluginMetaInfo(Properties properties) throws Exception { + packageZip.write(PROD_PLUGIN_META_PATH, outputStream->{ + properties.store(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), + Constant.PLUGIN_METE_COMMENTS); + }); + return PROD_PLUGIN_META_PATH; + } + + protected void resolveResourcesDefine() throws Exception{ + Set dependencyIndexNames = resolveDependencies(); + StringBuilder content = new StringBuilder(); + content.append(RESOURCES_DEFINE_DEPENDENCIES).append("\n"); + for (String dependencyIndexName : dependencyIndexNames) { + content.append(dependencyIndexName).append("\n"); + } + String loadMainResources = super.getLoadMainResources(); + if(!ObjectUtils.isEmpty(loadMainResources)){ + content.append(loadMainResources).append("\n"); + } + final byte[] bytes = content.toString().getBytes(StandardCharsets.UTF_8); + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)){ + packageZip.putInputStreamEntry(PROD_RESOURCES_DEFINE_PATH, byteArrayInputStream); + } + } + + protected Set resolveDependencies() throws Exception { + Set dependencies = repackageMojo.getFilterDependencies(); + String libDirEntryName = createLibEntry(); + Set dependencyIndexNames = new LinkedHashSet<>(dependencies.size()); + for (Artifact artifact : dependencies) { + if(filterArtifact(artifact)){ + continue; + } + File artifactFile = artifact.getFile(); + packageZip.writeDependency(artifactFile, libDirEntryName); + // fix 解决依赖前缀携带, lib 配置的前缀 + dependencyIndexNames.add(artifactFile.getName()); + } + return dependencyIndexNames; + } + + protected String createLibEntry() throws Exception { + String libDirEntryName = PROD_LIB_PATH; + packageZip.putDirEntry(libDirEntryName); + return libDirEntryName; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java new file mode 100755 index 0000000..f3dfdd6 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/CommonUtils.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.utils; + +import com.gitee.starblues.plugin.pack.filter.Exclude; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; +import java.util.jar.JarFile; + +/** + * Object 工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class CommonUtils { + + public final static String PLUGIN_FRAMEWORK_GROUP_ID = "com.gitee.starblues"; + public final static String PLUGIN_FRAMEWORK_ARTIFACT_ID = "spring-brick"; + + public final static String PLUGIN_FRAMEWORK_LOADER_ARTIFACT_ID = "spring-brick-loader"; + + public static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN); + + private CommonUtils(){} + + public static Exclude getPluginFrameworkExclude(){ + return Exclude.get(PLUGIN_FRAMEWORK_GROUP_ID, PLUGIN_FRAMEWORK_ARTIFACT_ID); + } + + public static boolean isPluginFramework(Artifact artifact){ + return Objects.equals(artifact.getGroupId(), PLUGIN_FRAMEWORK_GROUP_ID) + && Objects.equals(artifact.getArtifactId(), PLUGIN_FRAMEWORK_ARTIFACT_ID); + } + + public static boolean isPluginFrameworkLoader(Artifact artifact){ + return Objects.equals(artifact.getGroupId(), PLUGIN_FRAMEWORK_GROUP_ID) + && Objects.equals(artifact.getArtifactId(), PLUGIN_FRAMEWORK_LOADER_ARTIFACT_ID); + } + + public static JarFile getSourceJarFile(MavenProject mavenProject) { + File file = mavenProject.getArtifact().getFile(); + try { + return new JarFile(file); + } catch (Exception e){ + return null; + } + } + + public static String getDateTime() { + return DATE_TIME_FORMATTER.format(LocalDateTime.now()); + } + + public static void deleteFile(File rootFile) throws MojoFailureException { + try { + if(rootFile == null){ + return; + } + if(!rootFile.exists()){ + return; + } + if(rootFile.isFile()){ + FileUtils.delete(rootFile); + } else { + FileUtils.deleteDirectory(rootFile); + } + } catch (Exception e){ + e.printStackTrace(); + throw new MojoFailureException("Delete file '" + rootFile.getPath() + "' failure. " + e.getMessage()); + } + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java new file mode 100755 index 0000000..7fa1c46 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageJar.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.utils; + +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.jar.JarArchiveEntry; +import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.file.Files; + +/** + * jar 打包工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PackageJar extends PackageZip{ + public PackageJar(File file) throws Exception { + super(file); + } + + public PackageJar(String outputDirectory, String packageName) throws Exception { + super(outputDirectory, packageName); + } + + @Override + protected String getPackageFileSuffix() { + return "jar"; + } + + @Override + protected ArchiveOutputStream getOutputStream(File packFile) throws Exception { + return new JarArchiveOutputStream(Files.newOutputStream(packFile.toPath())); + } + + @Override + protected ZipArchiveEntry getArchiveEntry(String name) { + return new JarArchiveEntry(name); + } +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java new file mode 100755 index 0000000..96df2df --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/java/com/gitee/starblues/plugin/pack/utils/PackageZip.java @@ -0,0 +1,249 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.plugin.pack.utils; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.UnixStat; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.nio.file.Files; +import java.util.Enumeration; +import java.util.jar.Manifest; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; + +import static com.gitee.starblues.common.PackageStructure.*; + +/** + * zip 打包工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PackageZip implements Closeable{ + + + private static final int UNIX_FILE_MODE = UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM; + + private static final int UNIX_DIR_MODE = UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM; + + private final File file; + private final ArchiveOutputStream outputStream; + + + public PackageZip(File file) throws Exception { + this.file = file; + this.outputStream = getOutputStream(file); + } + + public PackageZip(String outputDirectory, String packageName) throws Exception{ + String rootPath = FilesUtils.joiningFilePath(outputDirectory, packageName); + this.file = getPackageFile(rootPath); + this.outputStream = getOutputStream(file); + } + + public File getFile(){ + return file; + } + + public String getFileName(){ + return file.getName(); + } + + protected File getPackageFile(String rootPath) throws Exception { + String fileSuffix = getPackageFileSuffix(); + File file = new File(rootPath + "." + fileSuffix); + CommonUtils.deleteFile(file); + if(file.createNewFile()){ + return file; + } + throw new IOException("Create file '" + file.getPath() + "' failure."); + } + + protected String getPackageFileSuffix(){ + return "zip"; + } + + protected ArchiveOutputStream getOutputStream(File packFile) throws Exception { + return new ZipArchiveOutputStream(Files.newOutputStream(packFile.toPath())); + } + + public void copyDirToPackage(File rootDir, String packageDir) throws Exception { + if(packageDir == null){ + packageDir = rootDir.getName(); + } + if (rootDir.isDirectory()) { + File[] childFiles = rootDir.listFiles(); + if(ObjectUtils.isEmpty(packageDir)){ + packageDir = ""; + } else { + packageDir = packageDir + "/"; + putDirEntry(packageDir); + } + if(childFiles == null){ + return; + } + for (File childFile : childFiles) { + copyDirToPackage(childFile, packageDir + childFile.getName()); + } + } else { + putFileEntry(rootDir, packageDir); + } + } + + public void copyZipToPackage(File sourceZipFile) throws Exception { + if(sourceZipFile == null || !sourceZipFile.exists()){ + return; + } + try (ZipFile zipFile = new ZipFile(sourceZipFile)){ + Enumeration entries = zipFile.getEntries(); + while (entries.hasMoreElements()){ + ZipArchiveEntry zipArchiveEntry = entries.nextElement(); + String name = zipArchiveEntry.getName(); + if(name.contains(PackageStructure.META_INF_NAME)){ + // 不拷贝 mate-inf + continue; + } + if(zipArchiveEntry.isDirectory()){ + putDirEntry(name); + } else { + try (InputStream inputStream = zipFile.getInputStream(zipArchiveEntry)){ + putInputStreamEntry(name, inputStream); + } + } + } + } + } + + public String writeDependency(File dependencyFile, String libDirEntryName) throws Exception { + String indexName = libDirEntryName + dependencyFile.getName(); + ZipArchiveEntry entry = getArchiveEntry(indexName); + entry.setTime(System.currentTimeMillis()); + entry.setUnixMode(indexName.endsWith("/") ? UNIX_DIR_MODE : UNIX_FILE_MODE); + entry.getGeneralPurposeBit().useUTF8ForNames(true); + try(FileInputStream inputStream = new FileInputStream(dependencyFile)){ + new CrcAndSize(inputStream).setupStoredEntry(entry); + } + try (FileInputStream inputStream = new FileInputStream(dependencyFile)){ + outputStream.putArchiveEntry(entry); + IOUtils.copy(inputStream, outputStream); + outputStream.closeArchiveEntry(); + } + return indexName; + } + + public void putFileEntry(File destFile, String rootDir) throws Exception { + if(!destFile.exists()){ + throw new FileNotFoundException("Not found file : " + destFile.getPath()); + } + outputStream.putArchiveEntry(getArchiveEntry(rootDir)); + FileUtils.copyFile(destFile, outputStream); + outputStream.closeArchiveEntry(); + } + + public void putInputStreamEntry(String name, InputStream inputStream) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + IOUtils.copy(inputStream, outputStream); + outputStream.closeArchiveEntry(); + } + + public void write(String name, String content) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + IOUtils.write(content, outputStream, CHARSET_NAME); + outputStream.closeArchiveEntry(); + } + + public void write(String name, Writer writer) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + writer.write(outputStream); + outputStream.closeArchiveEntry(); + } + + public void write(String name, File file) throws Exception { + outputStream.putArchiveEntry(getArchiveEntry(name)); + try (FileInputStream fileInputStream = new FileInputStream(file)){ + IOUtils.copy(fileInputStream, outputStream); + outputStream.closeArchiveEntry(); + } + } + + public void writeManifest(Manifest manifest) throws Exception { + putDirEntry(META_INF_NAME + SEPARATOR); + write(PROD_MANIFEST_PATH, manifest::write); + } + + public void putDirEntry(String dir) throws IOException { + outputStream.putArchiveEntry(getArchiveEntry(dir)); + outputStream.closeArchiveEntry(); + } + + protected ZipArchiveEntry getArchiveEntry(String name){ + return new ZipArchiveEntry(name); + } + + + @Override + public void close() throws IOException { + outputStream.finish(); + outputStream.close(); + } + + private static class CrcAndSize { + + private static final int BUFFER_SIZE = 32 * 1024; + + private final CRC32 crc = new CRC32(); + + private long size; + + CrcAndSize(InputStream inputStream) throws IOException { + load(inputStream); + } + + private void load(InputStream inputStream) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + this.crc.update(buffer, 0, bytesRead); + this.size += bytesRead; + } + } + + void setupStoredEntry(ZipArchiveEntry entry) { + entry.setSize(this.size); + entry.setCompressedSize(this.size); + entry.setCrc(this.crc.getValue()); + entry.setMethod(ZipEntry.STORED); + } + + } + + @FunctionalInterface + public interface Writer{ + void write(ArchiveOutputStream outputStream) throws Exception; + } + +} diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml b/iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml new file mode 100755 index 0000000..ba90682 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/com.gitee.starblues.springboot-plugin-maven-packager/plugin-help.xml @@ -0,0 +1,168 @@ + + + + Spring Boot Plugin Maven Packager + com.gitee.starblues + spring-brick-maven-packager + 3.1.4 + spring-brick-packager + false + true + + + repackage + 重新打包 + compile+runtime + false + true + false + false + false + true + package + com.gitee.starblues.plugin.pack.RepackageMojo + java + per-lookup + once-per-session + 3.0.0 + compile+runtime + true + + + project + org.apache.maven.project.MavenProject + 3.0.0 + true + false + 当前项目 + + + outputDirectory + java.io.File + 3.0.0 + true + true + 打包输出目录地址 + + + includes + java.util.List + 3.0.0 + false + true + 包含依赖定义 + + + excludes + java.util.List + 3.0.0 + false + true + 排除依赖定义 + + + skip + boolean + 3.0.0 + false + true + 跳过执行 + + + mode + string + 3.0.0 + true + true + 打包模式: dev/prod ,默认为dev + + + pluginInfo + com.gitee.starblues.plugin.pack.PluginInfo + 3.0.0 + false + true + 插件信息 + + + loadMainResourcePattern + com.gitee.starblues.plugin.pack.LoadMainResourcePattern + 3.0.0 + false + true + 从主程序加载资源的定义 + + + devConfig + com.gitee.starblues.plugin.pack.dev.DevConfig + 3.0.0 + false + true + dev打包模式配置 + + + prodConfig + com.gitee.starblues.plugin.pack.prod.ProdConfig + 3.0.0 + false + true + prod打包模式配置 + + + mainConfig + com.gitee.starblues.plugin.pack.main.MainConfig + 3.0.0 + false + true + main打包模式配置 + + + loadToMain + com.gitee.starblues.plugin.pack.LoadToMain + 3.0.0 + false + true + 加载到主程序的依赖 + + + encryptConfig + com.gitee.starblues.plugin.pack.encrypt.EncryptConfig + 3.0.1 + false + true + 加密配置 + + + includeSystemScope + boolean + 3.0.2 + false + true + 是否包含scope类型为system的依赖 + + + + + + + + + + ${springboot-plugin.includes} + ${springboot-plugin.excludes} + + + + + + + + + org.apache.maven.project.MavenProjectHelper + projectHelper + + + + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml b/iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml new file mode 100755 index 0000000..ba90682 --- /dev/null +++ b/iot-spring-brick/spring-brick-maven-packager/src/main/resources/META-INF/maven/plugin.xml @@ -0,0 +1,168 @@ + + + + Spring Boot Plugin Maven Packager + com.gitee.starblues + spring-brick-maven-packager + 3.1.4 + spring-brick-packager + false + true + + + repackage + 重新打包 + compile+runtime + false + true + false + false + false + true + package + com.gitee.starblues.plugin.pack.RepackageMojo + java + per-lookup + once-per-session + 3.0.0 + compile+runtime + true + + + project + org.apache.maven.project.MavenProject + 3.0.0 + true + false + 当前项目 + + + outputDirectory + java.io.File + 3.0.0 + true + true + 打包输出目录地址 + + + includes + java.util.List + 3.0.0 + false + true + 包含依赖定义 + + + excludes + java.util.List + 3.0.0 + false + true + 排除依赖定义 + + + skip + boolean + 3.0.0 + false + true + 跳过执行 + + + mode + string + 3.0.0 + true + true + 打包模式: dev/prod ,默认为dev + + + pluginInfo + com.gitee.starblues.plugin.pack.PluginInfo + 3.0.0 + false + true + 插件信息 + + + loadMainResourcePattern + com.gitee.starblues.plugin.pack.LoadMainResourcePattern + 3.0.0 + false + true + 从主程序加载资源的定义 + + + devConfig + com.gitee.starblues.plugin.pack.dev.DevConfig + 3.0.0 + false + true + dev打包模式配置 + + + prodConfig + com.gitee.starblues.plugin.pack.prod.ProdConfig + 3.0.0 + false + true + prod打包模式配置 + + + mainConfig + com.gitee.starblues.plugin.pack.main.MainConfig + 3.0.0 + false + true + main打包模式配置 + + + loadToMain + com.gitee.starblues.plugin.pack.LoadToMain + 3.0.0 + false + true + 加载到主程序的依赖 + + + encryptConfig + com.gitee.starblues.plugin.pack.encrypt.EncryptConfig + 3.0.1 + false + true + 加密配置 + + + includeSystemScope + boolean + 3.0.2 + false + true + 是否包含scope类型为system的依赖 + + + + + + + + + + ${springboot-plugin.includes} + ${springboot-plugin.excludes} + + + + + + + + + org.apache.maven.project.MavenProjectHelper + projectHelper + + + + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick/pom.xml b/iot-spring-brick/spring-brick/pom.xml new file mode 100755 index 0000000..eeb8f6d --- /dev/null +++ b/iot-spring-brick/spring-brick/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + + com.gitee.starblues + iot-spring-brick + 3.1.4 + + + spring-brick + jar + + 核心集成包, 用于框架集成 + + + 4.0.1 + 0.9.0 + 4.11 + 5.3.27 + + + + + com.gitee.starblues + spring-brick-common + ${project.version} + + + + com.gitee.starblues + spring-brick-loader + ${project.version} + + + + org.slf4j + slf4j-api + + + + commons-io + commons-io + + + + com.github.zafarkhaja + java-semver + ${java-semver.version} + + + + org.springframework.boot + spring-boot + ${spring-boot.version} + provided + true + + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.springframework + spring-webmvc + ${spring-web.version} + + + + javax.servlet + javax.servlet-api + ${javax.servlet-api.version} + provided + true + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + + + \ No newline at end of file diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java new file mode 100755 index 0000000..c043b03 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Caller.java @@ -0,0 +1,60 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.annotation; + + +import java.lang.annotation.*; + +/** + * 调用者的注解。配合 @Supplier 注解使用, 两者结合实现插件中的方法调用。 + * + * @author starBlues + * @version 2.4.0 + * @version 2.4.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Caller { + + /** + * 调用者的全局唯一key. 也就是Supplier 中定义的key. + * @return String + */ + String value(); + + /** + * 可指定调用哪一个插件 + * @return 插件id + */ + String pluginId() default ""; + + /** + * 调用者方法注解。配合 @Supper.Method 使用。如果不定义, 则以方法名称为准。 + */ + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @interface Method { + /** + * 方法名 + * @return String + */ + String value(); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java new file mode 100755 index 0000000..90f4460 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Extract.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.annotation; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +/** + * 基于业务的扩展注解 + * + * @author starBlues + * @since 2.4.4 + * @version 2.4.4 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Extract { + + /** + * 指定 Component Bean 名称 + * @return component name + */ + @AliasFor(annotation = Component.class) + String value() default ""; + + /** + * 业务 + * @return 业务标志 + */ + String bus(); + + /** + * 场景 + * @return 场景标志 + */ + String scene() default ""; + + /** + * 用例 + * @return 用例标志 + */ + String useCase() default ""; + + /** + * 不同插件存在同一业务时, 用于指定优先级别. 数字越大, 优先级别越高 + * @return 优先级别 + */ + int order() default 0; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java new file mode 100755 index 0000000..1eba7e4 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/annotation/Supplier.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.annotation; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +/** + * 被调用类的提供者。配合 @Caller 注解使用, 两者结合实现插件中的方法调用。 + * + * @author starBlues + * @since 2.4.0 + * @version 2.4.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Supplier { + + /** + * 全局唯一key.全局不能重复 + * @return String + */ + @AliasFor(annotation = Component.class) + String value(); + + /** + * 被调用者的方法注解。配合 @Caller.Method 使用.如果不定义, 则以方法名称为准。 + */ + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @interface Method { + /** + * 方法名 + * @return String + */ + String value(); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java new file mode 100755 index 0000000..136e22c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginInsideInfo.java @@ -0,0 +1,143 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.utils.Assert; +import lombok.Setter; + +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 默认的内部PluginWrapperInside实现 + * @author starBlues + * + * @since 3.0.0 + * @version 3.1.1 + */ +public class DefaultPluginInsideInfo implements PluginInsideInfo { + + private final String pluginId; + private final InsidePluginDescriptor pluginDescriptor; + private PluginState pluginState; + private boolean isFollowInitial = false; + + private Date startTime; + private Date stopTime; + + private Supplier> extensionInfoSupplier = Collections::emptyMap; + + private ClassLoader classLoader = null; + + public DefaultPluginInsideInfo(InsidePluginDescriptor pluginDescriptor) { + this.pluginId = pluginDescriptor.getPluginId(); + this.pluginDescriptor = pluginDescriptor; + } + + @Override + public void setPluginState(PluginState pluginState) { + this.pluginState = Assert.isNotNull(pluginState, "pluginState 不能为空"); + resolveTime(pluginState); + } + + @Override + public void setFollowSystem() { + isFollowInitial = true; + } + + @Override + public void setExtensionInfoSupplier(Supplier> supplier) { + this.extensionInfoSupplier = supplier; + } + + @Override + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public Supplier> getExtensionInfoSupplier() { + return extensionInfoSupplier; + } + + @Override + public String getPluginId() { + return pluginId; + } + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + @Override + public PluginInfo toPluginInfo() { + return new PluginInfoFace(this); + } + + @Override + public String getPluginPath() { + return pluginDescriptor.getPluginPath(); + } + + @Override + public PluginState getPluginState() { + return pluginState; + } + + @Override + public Date getStartTime() { + return startTime; + } + + @Override + public Date getStopTime() { + return stopTime; + } + + @Override + public boolean isFollowSystem() { + return isFollowInitial; + } + + @Override + public Map getExtensionInfo() { + return extensionInfoSupplier.get(); + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + private void resolveTime(PluginState pluginState){ + if(pluginState == PluginState.STARTED || pluginState == PluginState.STARTED_FAILURE){ + startTime = new Date(); + stopTime = null; + } else if(pluginState == PluginState.STOPPED || pluginState == PluginState.STOPPED_FAILURE){ + stopTime = new Date(); + startTime = null; + } else { + startTime = null; + stopTime = null; + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java new file mode 100755 index 0000000..db6cfd6 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultPluginManager.java @@ -0,0 +1,674 @@ +/** + * Copyright [2019-Present] [starBlues] + *

+ * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.ComposePluginLauncherChecker; +import com.gitee.starblues.core.checker.DefaultPluginLauncherChecker; +import com.gitee.starblues.core.checker.DependencyPluginLauncherChecker; +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.scanner.ComposePathResolve; +import com.gitee.starblues.core.scanner.DevPathResolve; +import com.gitee.starblues.core.scanner.PathResolve; +import com.gitee.starblues.core.scanner.ProdPathResolve; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.DefaultPluginListenerFactory; +import com.gitee.starblues.integration.listener.PluginListenerFactory; +import com.gitee.starblues.utils.*; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * 抽象的插件管理者 + * + * @author starBlues + * @version 3.1.2 + * @since 3.0.0 + */ +public class DefaultPluginManager implements PluginManager { + + private final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); + + private final RealizeProvider provider; + private final IntegrationConfiguration configuration; + private final List pluginRootDirs; + + private final PathResolve pathResolve; + private final PluginBasicChecker basicChecker; + + protected final ComposePluginLauncherChecker launcherChecker; + + private final AtomicBoolean loaded = new AtomicBoolean(false); + + private final Map startedPlugins = new ConcurrentHashMap<>(); + private final Map resolvedPlugins = new ConcurrentHashMap<>(); + + protected PluginListenerFactory pluginListenerFactory; + + + private List sortedPluginIds; + + public DefaultPluginManager(RealizeProvider realizeProvider, IntegrationConfiguration configuration) { + this.provider = Assert.isNotNull(realizeProvider, "参数 realizeProvider 不能为空"); + this.configuration = Assert.isNotNull(configuration, "参数 configuration 不能为空"); + this.pluginRootDirs = resolvePath(configuration.pluginPath()); + this.pathResolve = getComposePathResolve(); + this.basicChecker = realizeProvider.getPluginBasicChecker(); + this.launcherChecker = getComposeLauncherChecker(realizeProvider); + setSortedPluginIds(configuration.sortInitPluginIds()); + } + + protected ComposePluginLauncherChecker getComposeLauncherChecker(RealizeProvider realizeProvider) { + ComposePluginLauncherChecker checker = new ComposePluginLauncherChecker(); + checker.add(new DefaultPluginLauncherChecker(realizeProvider, configuration)); + checker.add(new DependencyPluginLauncherChecker(this)); + return checker; + } + + protected ComposePathResolve getComposePathResolve() { + return new ComposePathResolve(new DevPathResolve(), new ProdPathResolve()); + } + + public void setSortedPluginIds(List sortedPluginIds) { + this.sortedPluginIds = sortedPluginIds; + } + + @Override + public List getPluginsRoots() { + return new ArrayList<>(pluginRootDirs); + } + + @Override + public String getDefaultPluginRoot() { + if (pluginRootDirs == null) { + return null; + } + return pluginRootDirs.stream().findFirst().orElseThrow(() -> { + return new PluginException("插件根路径未配置"); + }); + } + + @Override + public synchronized List loadPlugins() { + if (loaded.get()) { + throw new PluginException("不能重复调用: loadPlugins"); + } + try { + pluginListenerFactory = createPluginListenerFactory(); + if (ObjectUtils.isEmpty(pluginRootDirs)) { + log.warn("插件根目录为空, 无法加载插件."); + return Collections.emptyList(); + } + List scanPluginPaths = provider.getPluginScanner().scan(pluginRootDirs); + if (ObjectUtils.isEmpty(scanPluginPaths)) { + printOfNotFoundPlugins(); + return Collections.emptyList(); + } + Map pluginInfoMap = new LinkedHashMap<>(scanPluginPaths.size()); + boolean findException = false; + for (Path path : scanPluginPaths) { + try { + PluginInsideInfo pluginInfo = loadPlugin(path, false); + if (pluginInfo != null) { + pluginInfo.setFollowSystem(); + PluginInfo pluginInfoFace = pluginInfo.toPluginInfo(); + pluginListenerFactory.loadSuccess(pluginInfoFace); + pluginInfoMap.put(pluginInfo.getPluginId(), pluginInfoFace); + } + } catch (Throwable e) { + pluginListenerFactory.loadFailure(path, e); + log.error("加载插件包失败: {}. {}", path, e.getMessage(), e); + findException = true; + } + } + if (!findException && pluginInfoMap.isEmpty()) { + printOfNotFoundPlugins(); + } + return getSortPlugin(pluginInfoMap); + } finally { + loaded.set(true); + } + } + + protected PluginListenerFactory createPluginListenerFactory() { + return new DefaultPluginListenerFactory(); + } + + @Override + public boolean verify(Path pluginPath) { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + try (PluginDescriptorLoader pluginDescriptorLoader = provider.getPluginDescriptorLoader()) { + basicChecker.checkPath(pluginPath); + PluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(pluginPath); + return pluginDescriptor != null; + } catch (Throwable e) { + log.error("插件包校验失败: {}", pluginPath, e); + return false; + } + } + + @Override + public PluginInsideInfo parse(Path pluginPath) throws PluginException { + if (pluginPath == null) { + throw new PluginException("解析文件不能为 null"); + } + PluginInsideInfo pluginInsideInfo = loadFromPath(pluginPath); + if (pluginInsideInfo == null) { + throw new PluginException("非法插件包: " + pluginPath); + } + pluginInsideInfo.setPluginState(PluginState.PARSED); + return pluginInsideInfo; + } + + @Override + public PluginInsideInfo scanParse(Path pluginPath) throws PluginException { + if (pluginPath == null) { + throw new PluginException("解析文件不能为 null"); + } + List scanList = new ArrayList<>(1); + scanList.add(pluginPath.toString()); + List scanPluginPaths = provider.getPluginScanner().scan(scanList); + for (Path scanPluginPath : scanPluginPaths) { + // 解析插件 + try { + PluginInsideInfo pluginInsideInfo = loadFromPath(scanPluginPath); + if (pluginInsideInfo != null) { + return pluginInsideInfo; + } + } catch (Exception e) { + // 忽略 + } + } + return null; + } + + @Override + public synchronized PluginInsideInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + Path sourcePluginPath = pluginPath; + File unpackPluginFile = null; + try { + if (unpackPlugin) { + unpackPluginFile = PluginFileUtils.decompressZip(pluginPath.toString(), configuration.uploadTempPath()); + pluginPath = unpackPluginFile.toPath(); + } + // 拷贝插件到root目录 + pluginPath = copyPluginToPluginRootDir(pluginPath); + PluginInsideInfo pluginInsideInfo = scanParse(pluginPath); + if (pluginInsideInfo == null) { + pluginListenerFactory.loadFailure(sourcePluginPath, new PluginException("Not found PluginInsideInfo")); + throw new PluginException("非法插件包: " + sourcePluginPath); + } + // 检查是否存在当前插件 + PluginInsideInfo plugin = getPlugin(pluginInsideInfo.getPluginId()); + if (plugin != null) { + // 已经存在该插件 + unLoad(plugin.getPluginId()); + } + pluginInsideInfo = loadPlugin(Paths.get(pluginInsideInfo.getPluginPath()), false); + PluginInfo pluginInfoFace = pluginInsideInfo.toPluginInfo(); + pluginListenerFactory.loadSuccess(pluginInfoFace); + return pluginInsideInfo; + } catch (Throwable e) { + PluginException pluginException = PluginException.getPluginException(e, () -> { + throw new PluginException("插件包加载失败: " + sourcePluginPath, e); + }); + pluginListenerFactory.loadFailure(sourcePluginPath, pluginException); + if (unpackPluginFile != null) { + FileUtils.deleteQuietly(unpackPluginFile); + } + FileUtils.deleteQuietly(pluginPath.toFile()); + throw pluginException; + } + } + + @Override + public synchronized void unLoad(String pluginId) { + Assert.isNotNull(pluginId, "参数pluginId不能为空"); + PluginInsideInfo pluginInsideInfo = resolvedPlugins.get(pluginId); + if (!resolvedPlugins.containsKey(pluginId)) { + throw new PluginException("没有发现插件: " + pluginId); + } + resolvedPlugins.remove(pluginId); + pluginListenerFactory.unLoadSuccess(pluginInsideInfo.toPluginInfo()); + LogUtils.info(log, pluginInsideInfo.getPluginDescriptor(), "卸载成功"); + } + + @Override + public synchronized PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + PluginInfo loadPluginInfo = load(pluginPath, unpackPlugin); + if (loadPluginInfo == null) { + throw new PluginException("插件包安装失败: " + pluginPath); + } + PluginInsideInfo pluginInsideInfo = resolvedPlugins.get(loadPluginInfo.getPluginId()); + PluginInfo pluginInfo = pluginInsideInfo.toPluginInfo(); + try { + start(pluginInsideInfo); + pluginListenerFactory.startSuccess(pluginInfo); + log.info("插件[{}]安装成功", MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor())); + return pluginInsideInfo.toPluginInfo(); + } catch (Throwable e) { + if (e instanceof PluginDisabledException) { + throw (PluginDisabledException) e; + } + PluginException pluginException = PluginException.getPluginException(e, () -> { + unLoad(loadPluginInfo.getPluginId()); + // fix https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I5GJO9 + return new PluginException("插件包[ " + pluginPath + " ]安装: " + e.getMessage(), e); + }); + pluginListenerFactory.startFailure(pluginInfo, pluginException); + throw pluginException; + } + } + + @Override + public synchronized void uninstall(String pluginId) throws PluginException { + uninstall(pluginId, PluginCloseType.UNINSTALL); + } + + + @Override + public synchronized PluginInfo upgrade(Path pluginPath, boolean unpackPlugin) throws PluginException { + Assert.isNotNull(pluginPath, "参数pluginPath不能为空"); + // 解析插件包 + PluginInfo upgradePlugin = parse(pluginPath); + if (upgradePlugin == null) { + throw new PluginException("非法插件包: " + pluginPath); + } + // 检查插件是否被禁用 + PluginDisabledException.checkDisabled(upgradePlugin, configuration, "更新"); + String pluginId = upgradePlugin.getPluginId(); + // 得到旧插件 + PluginInsideInfo oldPlugin = getPlugin(pluginId); + // 检查插件版本 + PluginDescriptor upgradePluginDescriptor = upgradePlugin.getPluginDescriptor(); +// checkVersion(oldPlugin.getPluginDescriptor(), upgradePluginDescriptor); + if (oldPlugin.getPluginState() == PluginState.STARTED) { + // 如果插件被启动, 则卸载旧的插件 + uninstall(pluginId, PluginCloseType.UPGRADE_UNINSTALL); + } else if (oldPlugin.getPluginState() == PluginState.LOADED) { + // 如果插件被load + unLoad(pluginId); + } + try { + pluginPath = copyPluginToPluginRootDir(pluginPath); + // 安装新插件 + install(pluginPath, unpackPlugin); + log.info("更新插件[{}]成功", MsgUtils.getPluginUnique(upgradePluginDescriptor)); + return upgradePlugin; + } catch (Throwable e) { + throw PluginException.getPluginException(e, () -> + new PluginException(upgradePluginDescriptor, "更新", e)); + } + } + + @Override + public synchronized PluginInfo start(String pluginId) throws PluginException { + if (ObjectUtils.isEmpty(pluginId)) { + return null; + } + PluginInsideInfo pluginInsideInfo = getPlugin(pluginId); + if (pluginInsideInfo == null) { + throw new PluginException("没有发现插件: " + pluginId); + } + PluginInfo pluginInfo = pluginInsideInfo.toPluginInfo(); + try { + start(pluginInsideInfo); + log.info("插件[{}]启动成功", MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor())); + pluginListenerFactory.startSuccess(pluginInfo); + return pluginInfo; + } catch (Throwable e) { + PluginException pluginException = PluginException.getPluginException(e, + () -> new PluginException(pluginInsideInfo.getPluginDescriptor(), "启动", e)); + pluginListenerFactory.startFailure(pluginInfo, pluginException); + throw pluginException; + } + } + + @Override + public synchronized PluginInfo stop(String pluginId) throws PluginException { + if (ObjectUtils.isEmpty(pluginId)) { + return null; + } + PluginInsideInfo pluginInsideInfo = getPlugin(pluginId); + if (pluginInsideInfo == null) { + throw new PluginException("没有发现插件: " + pluginId); + } + PluginInfo pluginInfo = pluginInsideInfo.toPluginInfo(); + try { + stop(pluginInsideInfo, PluginCloseType.STOP); + log.info("停止插件[{}]成功", MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor())); + pluginListenerFactory.stopSuccess(pluginInfo); + return pluginInfo; + } catch (Throwable e) { + PluginException pluginException = PluginException.getPluginException(e, + () -> new PluginException(pluginInsideInfo.getPluginDescriptor(), "停止", e)); + pluginListenerFactory.stopFailure(pluginInfo, pluginException); + throw pluginException; + } + } + + @Override + public synchronized PluginInsideInfo getPluginInfo(String pluginId) { + if (ObjectUtils.isEmpty(pluginId)) { + return null; + } + PluginInsideInfo wrapperInside = startedPlugins.get(pluginId); + if (wrapperInside == null) { + wrapperInside = resolvedPlugins.get(pluginId); + } + return wrapperInside; + } + + @Override + public synchronized List getPluginInfos() { + List pluginDescriptors = new ArrayList<>( + resolvedPlugins.size() + startedPlugins.size()); + pluginDescriptors.addAll(startedPlugins.values()); + pluginDescriptors.addAll(resolvedPlugins.values()); + return pluginDescriptors; + } + + /** + * 卸载插件 + * + * @param pluginId 插件id + * @param closeType 关闭类型 + * @throws PluginException 卸载异常 + */ + protected void uninstall(String pluginId, PluginCloseType closeType) throws PluginException { + Assert.isNotNull(pluginId, "参数pluginId不能为空"); + PluginInsideInfo wrapperInside = getPlugin(pluginId); + if (wrapperInside == null) { + throw new PluginException("没有发现插件: " + pluginId); + } + PluginInfo pluginInfo = wrapperInside.toPluginInfo(); + if (wrapperInside.getPluginState() == PluginState.STARTED) { + try { + stop(wrapperInside, closeType); + pluginListenerFactory.stopSuccess(pluginInfo); + } catch (Throwable e) { + PluginException pluginException = PluginException.getPluginException(e, + () -> new PluginException("停止", pluginId, e)); + pluginListenerFactory.stopFailure(pluginInfo, pluginException); + throw pluginException; + } + } + startedPlugins.remove(pluginId); + unLoad(pluginId); + LogUtils.info(log, wrapperInside.getPluginDescriptor(), "卸载成功"); + } + + /** + * 加载插件信息 + * + * @param pluginPath 插件路径 + * @param resolvePath 是否直接解析路径 + * @return 插件内部细腻些 + */ + protected PluginInsideInfo loadPlugin(Path pluginPath, boolean resolvePath) { + if (resolvePath) { + Path sourcePluginPath = pluginPath; + pluginPath = pathResolve.resolve(pluginPath); + if (pluginPath == null) { + throw new PluginException("未发现插件: " + sourcePluginPath); + } + } + PluginInsideInfo pluginInsideInfo = loadFromPath(pluginPath); + if (pluginInsideInfo == null) { + return null; + } + String pluginId = pluginInsideInfo.getPluginId(); + if (resolvedPlugins.containsKey(pluginId)) { + throw new PluginException(pluginInsideInfo.getPluginDescriptor(), "已经被加载"); + } + // 检查当前插件版本号是否合法 + provider.getVersionInspector().check(pluginInsideInfo.getPluginDescriptor().getPluginVersion()); + resolvedPlugins.put(pluginId, pluginInsideInfo); + LogUtils.info(log, pluginInsideInfo.getPluginDescriptor(), "加载成功"); + return pluginInsideInfo; + } + + protected PluginInsideInfo loadFromPath(Path pluginPath) { + try { + basicChecker.checkPath(pluginPath); + } catch (Throwable e) { + throw PluginException.getPluginException(e, () -> { + return new PluginException("非法插件包. " + e.getMessage(), e); + }); + } + + try (PluginDescriptorLoader pluginDescriptorLoader = provider.getPluginDescriptorLoader()) { + InsidePluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(pluginPath); + if (pluginDescriptor == null) { + return null; + } + String pluginId = pluginDescriptor.getPluginId(); + PluginInsideInfo pluginInsideInfo = new DefaultPluginInsideInfo(pluginDescriptor); + if (configuration.isDisabled(pluginId)) { + pluginInsideInfo.setPluginState(PluginState.DISABLED); + } else { + pluginInsideInfo.setPluginState(PluginState.LOADED); + } + return pluginInsideInfo; + } catch (Throwable e) { + throw PluginException.getPluginException(e, () -> new PluginException("加载插件失败")); + } + } + + /** + * 拷贝插件文件到插件根目录 + * + * @param pluginPath 源插件文件路径 + * @return 拷贝后的插件路径 + * @throws IOException IO 异常 + */ + protected Path copyPluginToPluginRootDir(Path pluginPath) throws IOException { + if (configuration.isDev()) { + // 开发环境不拷贝 + return pluginPath; + } + File targetFile = pluginPath.toFile(); + if (!targetFile.exists()) { + throw new PluginException("不存在插件文件: " + pluginPath); + } + String fileName = targetFile.getName(); + Path resultPath = null; + // 不在插件目录 + File pluginFile = pluginPath.toFile(); + File pluginRootDir = new File(getDefaultPluginRoot()); + targetFile = Paths.get(FilesUtils.joiningFilePath(pluginRootDir.getPath(), fileName)).toFile(); + if (targetFile.exists()) { + throw new PluginException("插件包已存在"); + } + if (pluginFile.isFile()) { + FileUtils.copyFile(pluginFile, targetFile); + resultPath = targetFile.toPath(); + } else { + FileUtils.copyDirectory(pluginFile, targetFile); + resultPath = targetFile.toPath(); + } + return resultPath; + } + + /** + * 统一启动插件操作 + * + * @param pluginInsideInfo PluginInsideInfo + * @throws Exception 启动异常 + */ + protected void start(PluginInsideInfo pluginInsideInfo) throws Exception { + Assert.isNotNull(pluginInsideInfo, "pluginInsideInfo 参数不能为空"); + launcherChecker.checkCanStart(pluginInsideInfo); + pluginInsideInfo.setPluginState(PluginState.STARTED); + startFinish(pluginInsideInfo); + } + + /** + * 启动完成后的操作 + * + * @param pluginInsideInfo pluginInsideInfo + */ + protected void startFinish(PluginInsideInfo pluginInsideInfo) { + String pluginId = pluginInsideInfo.getPluginId(); + startedPlugins.put(pluginId, pluginInsideInfo); + resolvedPlugins.remove(pluginId); + } + + + /** + * 统一停止插件操作 + * + * @param pluginInsideInfo PluginInsideInfo + * @param closeType 停止类型 + * @throws Exception 启动异常 + */ + protected void stop(PluginInsideInfo pluginInsideInfo, PluginCloseType closeType) throws Exception { + launcherChecker.checkCanStop(pluginInsideInfo); + pluginInsideInfo.setPluginState(PluginState.STOPPED); + stopFinish(pluginInsideInfo); + } + + /** + * 停止完成操作 + * + * @param pluginInsideInfo pluginInsideInfo + */ + protected void stopFinish(PluginInsideInfo pluginInsideInfo) { + String pluginId = pluginInsideInfo.getPluginId(); + resolvedPlugins.put(pluginId, pluginInsideInfo); + startedPlugins.remove(pluginId); + } + + /** + * 根据配置重新排序插件 + * + * @param pluginInfos 未排序的查询信息 + * @return 排序的插件信息 + */ + protected List getSortPlugin(Map pluginInfos) { + if (ObjectUtils.isEmpty(pluginInfos)) { + return Collections.emptyList(); + } + if (ObjectUtils.isEmpty(sortedPluginIds)) { + return new ArrayList<>(pluginInfos.values()); + } + List sortPluginInfos = new ArrayList<>(); + for (String sortedPluginId : sortedPluginIds) { + PluginInfo pluginInfo = pluginInfos.get(sortedPluginId); + if (pluginInfo != null) { + sortPluginInfos.add(pluginInfo); + pluginInfos.remove(sortedPluginId); + } + } + sortPluginInfos.addAll(pluginInfos.values()); + return sortPluginInfos; + } + + + protected PluginInsideInfo getPlugin(String pluginId) { + PluginInsideInfo wrapperInside = startedPlugins.get(pluginId); + if (wrapperInside == null) { + wrapperInside = resolvedPlugins.get(pluginId); + } + return wrapperInside; + } + + /** + * 检查是否目录中是否存在同名插件 + * + * @param dirFile 目录文件 + * @param pluginFileName 检查的插件名 + */ + private void checkExistFile(File dirFile, String pluginFileName) { + if (ResourceUtils.existFile(dirFile, pluginFileName)) { + // 插件根目录存在同名插件文件 + throw getExistFileException(dirFile.getPath(), pluginFileName); + } + } + + private PluginException getExistFileException(String rootPath, String pluginFileName) { + return new PluginException("插件目录[" + rootPath + "]存在同名文件: " + pluginFileName); + } + + /** + * 检查比较插件版本 + * + * @param oldPlugin 旧插件信息 + * @param newPlugin 新插件信息 + */ + protected void checkVersion(PluginDescriptor oldPlugin, PluginDescriptor newPlugin) { + int compareVersion = provider.getVersionInspector().compareTo(oldPlugin.getPluginVersion(), + newPlugin.getPluginVersion()); + if (compareVersion >= 0) { + throw new PluginException("新插件包版本[" + MsgUtils.getPluginUnique(newPlugin) + "]必须大于" + + "旧插件版本[" + MsgUtils.getPluginUnique(oldPlugin) + "]"); + } + } + + private List resolvePath(List path) { + if (ObjectUtils.isEmpty(path)) { + return Collections.emptyList(); + } else { + File file = new File(""); + String absolutePath = file.getAbsolutePath(); + return path.stream() + .filter(p -> !ObjectUtils.isEmpty(p)) + .map(p -> FilesUtils.resolveRelativePath(absolutePath, p)) + .collect(Collectors.toList()); + } + } + + /** + * 没有扫描到插件时的日志打印 + */ + private void printOfNotFoundPlugins() { + StringBuilder warn = new StringBuilder(); + warn.append("以下路径未发现插件: \n"); + if (pluginRootDirs.size() == 1) { + warn.append(pluginRootDirs.get(0)).append("\n"); + } else { + for (int i = 0; i < pluginRootDirs.size(); i++) { + warn.append(i + 1).append(". ").append(pluginRootDirs.get(i)).append("\n"); + } + } + warn.append("请检查路径是否合适.\n"); + warn.append("请检查配置[plugin.runMode]是否合适.\n"); + if (provider.getRuntimeMode() == RuntimeMode.DEV) { + warn.append("请检查插件包是否编译.\n"); + } else { + warn.append("请检查插件是否合法.\n"); + } + log.warn(warn.toString()); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java new file mode 100755 index 0000000..f716d41 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/DefaultRealizeProvider.java @@ -0,0 +1,115 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.ComposePluginBasicChecker; +import com.gitee.starblues.core.checker.DefaultPluginBasicChecker; +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.ComposeDescriptorLoader; +import com.gitee.starblues.core.descriptor.DevPluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.descriptor.ProdPluginDescriptorLoader; +import com.gitee.starblues.core.scanner.BasePluginScanner; +import com.gitee.starblues.core.scanner.DevPathResolve; +import com.gitee.starblues.core.scanner.PluginScanner; +import com.gitee.starblues.core.scanner.ProdPathResolve; +import com.gitee.starblues.core.version.SemverVersionInspector; +import com.gitee.starblues.core.version.VersionInspector; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.Assert; +import org.springframework.context.ApplicationContext; + +/** + * 默认的RealizeProvider实现 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class DefaultRealizeProvider implements RealizeProvider { + + private PluginScanner pluginScanner; + private PluginBasicChecker pluginBasicChecker; + private PluginDescriptorLoader pluginDescriptorLoader; + private VersionInspector versionInspector; + + protected final IntegrationConfiguration configuration; + protected final ApplicationContext applicationContext; + + public DefaultRealizeProvider(IntegrationConfiguration configuration, + ApplicationContext applicationContext){ + this.configuration = Assert.isNotNull(configuration, "参数 configuration 不能为空"); + this.applicationContext = Assert.isNotNull(applicationContext, "参数 configuration 不能为空"); + } + + @Override + public void init() { + BasePluginScanner basePluginScanner = new BasePluginScanner(); + if(configuration.environment() == RuntimeMode.DEV){ + basePluginScanner.setPathResolve(new DevPathResolve()); + } else { + basePluginScanner.setPathResolve(new ProdPathResolve()); + } + setPluginScanner(basePluginScanner); + setPluginBasicChecker(new ComposePluginBasicChecker(applicationContext)); + setPluginDescriptorLoader(new ComposeDescriptorLoader(applicationContext, pluginBasicChecker)); + setVersionInspector(new SemverVersionInspector()); + } + + public void setPluginScanner(PluginScanner pluginScanner) { + this.pluginScanner = Assert.isNotNull(pluginScanner, "pluginScanner 不能为空"); + } + + public void setPluginBasicChecker(PluginBasicChecker pluginBasicChecker) { + this.pluginBasicChecker = Assert.isNotNull(pluginBasicChecker, + "pluginBasicChecker 不能为空"); + } + + public void setPluginDescriptorLoader(PluginDescriptorLoader pluginDescriptorLoader) { + this.pluginDescriptorLoader = Assert.isNotNull(pluginDescriptorLoader, + "pluginDescriptorLoader 不能为空"); + } + + public void setVersionInspector(VersionInspector versionInspector) { + this.versionInspector = Assert.isNotNull(versionInspector, "versionInspector 不能为空"); + } + + @Override + public RuntimeMode getRuntimeMode() { + return configuration.environment(); + } + + @Override + public PluginScanner getPluginScanner() { + return Assert.isNotNull(pluginScanner, "PluginScanner 实现不能为空"); + } + + @Override + public PluginBasicChecker getPluginBasicChecker() { + return Assert.isNotNull(pluginBasicChecker, "pluginBasicChecker 实现不能为空"); + } + + @Override + public PluginDescriptorLoader getPluginDescriptorLoader() { + return Assert.isNotNull(pluginDescriptorLoader, "PluginDescriptorLoader 实现不能为空"); + } + + @Override + public VersionInspector getVersionInspector() { + return Assert.isNotNull(versionInspector, "VersionInspector 实现不能为空"); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginCloseType.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginCloseType.java new file mode 100755 index 0000000..0aef5e4 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginCloseType.java @@ -0,0 +1,27 @@ +package com.gitee.starblues.core; + +/** + * 插件关闭类型 + * + * @author starBlues + * @since 3.1.0 + * @version 3.1.0 + */ +public enum PluginCloseType { + + /** + * 直接操作停止 + */ + STOP, + + /** + * 卸载时停止 + */ + UNINSTALL, + + /** + * 升级时停止 + */ + UPGRADE_UNINSTALL + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginExtensionInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginExtensionInfo.java new file mode 100755 index 0000000..a741d01 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginExtensionInfo.java @@ -0,0 +1,21 @@ +package com.gitee.starblues.core; + +import java.util.Map; + +/** + * 自主实现插件的扩展信息 + * + * @author starBlues + * @version 3.1.0 + * @since 3.1.0 + */ +public interface PluginExtensionInfo { + + /** + * 实现返回扩展信息 + * @return 扩展信息Map + */ + Map extensionInfo(); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java new file mode 100755 index 0000000..aa43f41 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfo.java @@ -0,0 +1,91 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreType; +import com.gitee.starblues.core.descriptor.PluginDescriptor; + +import java.util.Date; +import java.util.Map; + +/** + * 插件信息 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public interface PluginInfo { + + /** + * 得到插件id + * @return String + */ + String getPluginId(); + + /** + * 得到插件描述 + * @return PluginDescriptor + */ + PluginDescriptor getPluginDescriptor(); + + /** + * 得到插件路径 + * @return Path + */ + String getPluginPath(); + + /** + * 得到插件状态 + * @return PluginState + */ + PluginState getPluginState(); + + /** + * 启动时间. 只有启动状态 {@link PluginState#STARTED} 才有值。 + * @return Date + */ + Date getStartTime(); + + /** + * 停止时间. 只有停止状态 {@link PluginState#STOPPED} 才有值。 + * @return Date + */ + Date getStopTime(); + + /** + * 是否跟随系统启动而加载的插件 + * @return true: 是, false: 否 + */ + boolean isFollowSystem(); + + /** + * 获取插件自主扩展信息 + * @return 扩展信息Map + */ + Map getExtensionInfo(); + + /** + * 获取插件的Classloader + * @return ClassLoader + */ + @JsonIgnore + ClassLoader getClassLoader(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java new file mode 100755 index 0000000..72e1144 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInfoFace.java @@ -0,0 +1,99 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.utils.Assert; + +import java.util.Date; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 外部 PluginWrapperFace + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class PluginInfoFace implements PluginInfo { + + private final PluginDescriptor pluginDescriptor; + private final PluginState pluginState; + private final boolean followSystem; + private final Supplier> extensionInfoSupplier; + private final ClassLoader classLoader; + + private final Date startTime; + private final Date stopTime; + + public PluginInfoFace(PluginInsideInfo pluginInsideInfo) { + Assert.isNotNull(pluginInsideInfo, "参数 pluginWrapperInside 不能为空"); + this.pluginDescriptor = pluginInsideInfo.getPluginDescriptor().toPluginDescriptor(); + this.pluginState = pluginInsideInfo.getPluginState(); + this.followSystem = pluginInsideInfo.isFollowSystem(); + this.extensionInfoSupplier = pluginInsideInfo.getExtensionInfoSupplier(); + this.startTime = pluginInsideInfo.getStartTime(); + this.stopTime = pluginInsideInfo.getStopTime(); + this.classLoader = pluginInsideInfo.getClassLoader(); + } + + @Override + public String getPluginId() { + return pluginDescriptor.getPluginId(); + } + + @Override + public PluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + @Override + public String getPluginPath() { + return pluginDescriptor.getPluginPath(); + } + + @Override + public PluginState getPluginState() { + return pluginState; + } + + @Override + public Date getStartTime() { + return startTime; + } + + @Override + public Date getStopTime() { + return stopTime; + } + + @Override + public boolean isFollowSystem() { + return followSystem; + } + + @Override + public Map getExtensionInfo() { + return extensionInfoSupplier.get(); + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java new file mode 100755 index 0000000..8499404 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginInsideInfo.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; + +import java.util.Map; +import java.util.function.Supplier; + +/** + * 内部的 PluginInfo + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public interface PluginInsideInfo extends PluginInfo { + + /** + * 设置插件状态 + * @param pluginState 插件状态 + */ + void setPluginState(PluginState pluginState); + + /** + * 设置是跟随系统启动而启动的插件 + */ + void setFollowSystem(); + + /** + * 设置插件扩展信息 + * @param supplier 插件扩展信息自主提供者 + */ + void setExtensionInfoSupplier(Supplier> supplier); + + /** + * 设置插件的 classLoader + * @param classLoader ClassLoader + */ + void setClassLoader(ClassLoader classLoader); + + /** + * 获取插件信息提供者 + * @return 插件扩展信息自主提供者 + */ + Supplier> getExtensionInfoSupplier(); + + /** + * 得到插件描述 + * @return PluginDescriptor + */ + @Override + InsidePluginDescriptor getPluginDescriptor(); + + /** + * 转换为外部插件信息 + * @return PluginInfo + */ + PluginInfo toPluginInfo(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java new file mode 100755 index 0000000..4318ede --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginLauncherManager.java @@ -0,0 +1,169 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.PluginLauncherChecker; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.exception.PluginProhibitStopException; +import com.gitee.starblues.core.launcher.plugin.DefaultPluginInteractive; +import com.gitee.starblues.core.launcher.plugin.PluginInteractive; +import com.gitee.starblues.core.launcher.plugin.PluginCoexistLauncher; +import com.gitee.starblues.core.launcher.plugin.PluginIsolationLauncher; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolvedFactory; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.DefaultPluginListenerFactory; +import com.gitee.starblues.integration.listener.PluginListenerFactory; +import com.gitee.starblues.loader.launcher.AbstractLauncher; +import com.gitee.starblues.loader.launcher.DevelopmentModeSetting; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.MainApplicationContextProxy; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.invoke.DefaultInvokeSupperCache; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可引导启动的插件管理者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class PluginLauncherManager extends DefaultPluginManager{ + + private final Map registryInfo = new ConcurrentHashMap<>(); + + + private final MainApplicationContext mainApplicationContext; + private final GenericApplicationContext mainGenericApplicationContext; + private final IntegrationConfiguration configuration; + private final InvokeSupperCache invokeSupperCache; + private final PluginLaunchInvolved pluginLaunchInvolved; + + public PluginLauncherManager(RealizeProvider realizeProvider, + GenericApplicationContext applicationContext, + IntegrationConfiguration configuration) { + super(realizeProvider, configuration); + this.mainApplicationContext = new MainApplicationContextProxy(applicationContext, applicationContext); + this.mainGenericApplicationContext = applicationContext; + this.configuration = configuration; + this.invokeSupperCache = new DefaultInvokeSupperCache(); + this.pluginLaunchInvolved = new PluginLaunchInvolvedFactory(); + addCustomPluginChecker(); + } + + private void addCustomPluginChecker(){ + List pluginCheckers = SpringBeanUtils.getBeans(mainGenericApplicationContext, + PluginLauncherChecker.class); + for (PluginLauncherChecker pluginChecker : pluginCheckers) { + super.launcherChecker.add(pluginChecker); + } + } + + @Override + protected PluginListenerFactory createPluginListenerFactory() { + return new DefaultPluginListenerFactory(mainGenericApplicationContext); + } + + @Override + public synchronized List loadPlugins() { + this.pluginLaunchInvolved.initialize(mainGenericApplicationContext, configuration); + return super.loadPlugins(); + } + + @Override + protected void start(PluginInsideInfo pluginInsideInfo) throws Exception { + launcherChecker.checkCanStart(pluginInsideInfo); + try { + InsidePluginDescriptor pluginDescriptor = pluginInsideInfo.getPluginDescriptor(); + PluginInteractive pluginInteractive = new DefaultPluginInteractive(pluginInsideInfo, + mainApplicationContext, configuration, invokeSupperCache); + AbstractLauncher pluginLauncher; + if(DevelopmentModeSetting.isolation()){ + pluginLauncher = new PluginIsolationLauncher(pluginInteractive, pluginLaunchInvolved); + } else if(DevelopmentModeSetting.coexist()){ + pluginLauncher = new PluginCoexistLauncher(pluginInteractive, pluginLaunchInvolved); + } else { + throw DevelopmentModeSetting.getUnknownModeException(); + } + SpringPluginHook springPluginHook = pluginLauncher.run(); + RegistryPluginInfo registryPluginInfo = new RegistryPluginInfo(pluginDescriptor, springPluginHook); + registryInfo.put(pluginDescriptor.getPluginId(), registryPluginInfo); + pluginInsideInfo.setPluginState(PluginState.STARTED); + super.startFinish(pluginInsideInfo); + } catch (Exception e){ + // 启动失败, 进行停止 + pluginInsideInfo.setPluginState(PluginState.STARTED_FAILURE); + throw e; + } + } + + @Override + protected void stop(PluginInsideInfo pluginInsideInfo, PluginCloseType closeType) throws Exception { + launcherChecker.checkCanStop(pluginInsideInfo); + String pluginId = pluginInsideInfo.getPluginId(); + RegistryPluginInfo registryPluginInfo = registryInfo.get(pluginId); + if(registryPluginInfo == null){ + throw new PluginException("没有发现插件 '" + pluginId + "' 信息"); + } + try { + SpringPluginHook springPluginHook = registryPluginInfo.getSpringPluginHook(); + // 校验是否可停止 + springPluginHook.stopVerify(); + // 关闭插件 + springPluginHook.close(closeType); + // 移除插件相互调用缓存的信息 + invokeSupperCache.remove(pluginId); + // 移除插件注册信息 + registryInfo.remove(pluginId); + super.stop(pluginInsideInfo, closeType); + } catch (Exception e){ + if(e instanceof PluginProhibitStopException){ + // 禁止停止时, 不设置插件状态 + throw e; + } + pluginInsideInfo.setPluginState(PluginState.STOPPED_FAILURE); + throw e; + } + } + + + static class RegistryPluginInfo{ + private final PluginDescriptor descriptor; + private final SpringPluginHook springPluginHook; + + private RegistryPluginInfo(PluginDescriptor descriptor, SpringPluginHook springPluginHook) { + this.descriptor = descriptor; + this.springPluginHook = springPluginHook; + } + + public PluginDescriptor getDescriptor() { + return descriptor; + } + + public SpringPluginHook getSpringPluginHook() { + return springPluginHook; + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java new file mode 100755 index 0000000..3f41aad --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginManager.java @@ -0,0 +1,144 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; +import java.util.List; + +/** + * 插件管理者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public interface PluginManager { + + /** + * 得到插件root目录 + * @return List + */ + List getPluginsRoots(); + + /** + * 得到插件默认的根路径 + * @return String + */ + String getDefaultPluginRoot(); + + /** + * 加载配置目录中全部插件 + * @return 加载的插件信息 + */ + List loadPlugins(); + + /** + * 校验插件jar包 + * @param pluginPath 插件包的路径 + * @return 成功: 返回true; 失败: 抛出异常或者返回false + * @throws PluginException 校验异常 + */ + boolean verify(Path pluginPath); + + /** + * 解析插件包 + * @param pluginPath 插件包路基 + * @return 解析的插件信息 + * @throws PluginException 插件异常 + */ + PluginInsideInfo parse(Path pluginPath) throws PluginException; + + /** + * 从某个目录扫描解析首个插件 + * @param pluginPath 插件路径 + * @return 解析的插件信息 + * @throws PluginException 插件异常 + */ + PluginInsideInfo scanParse(Path pluginPath) throws PluginException; + + /** + * 根据具体插件路径来加载插件. + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件文件 + * @return 加载的插件信息 + * @throws PluginException 插件异常 + */ + PluginInsideInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 卸载加载插件 + * @param pluginId 插件id + */ + void unLoad(String pluginId); + + /** + * 通过路径安装插件(会启用), 该插件文件必须存在于服务器 [适用于生产环境] + * 如果在插件目录存在同名的插件包, 系统会自动备份该插件包。备份文件命名规则为;[install-backup][时间]_原jar名.jar + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件包. (如果插件包为压缩包时生效) + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + */ + PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 卸载插件 + * @param pluginId 插件id + * @throws PluginException 插件异常 + */ + void uninstall(String pluginId) throws PluginException; + + /** + * 更新已经安装的插件 + * @param pluginPath 新版本插件路径 + * @param unpackPlugin 是否解压要更新的插件文件 + * @throws PluginException 插件异常 + * @return PluginInfo 更新的插件信息 + */ + PluginInfo upgrade(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 启动处于 RESOLVED 状态的插件 + * @param pluginId 插件id + * @return PluginDescriptor + * @throws PluginException 插件异常 + */ + PluginInfo start(String pluginId) throws PluginException; + + /** + * 停止启动的插件 + * @param pluginId 插件id + * @return PluginDescriptor + * @throws PluginException 插件异常 + */ + PluginInfo stop(String pluginId) throws PluginException; + + /** + * 根据插件id获取插件描述信息 + * @param pluginId 插件id + * @return PluginDescriptor + */ + PluginInsideInfo getPluginInfo(String pluginId); + + /** + * 得到全部的插件信息 + * @return List PluginWrapper + */ + List getPluginInfos(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java new file mode 100755 index 0000000..02ac658 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/PluginState.java @@ -0,0 +1,88 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +/** + * 插件状态枚举 + * @author starBlues + * + * @since 3.0.0 + * @version 3.0.0 + */ +public enum PluginState { + + /** + * 被禁用状态 + */ + DISABLED("DISABLED"), + + /** + * 被加载了 + */ + LOADED("LOADED"), + + /** + * 启动状态 + */ + STARTED("STARTED"), + + /** + * 启动失败状态 + */ + STARTED_FAILURE("STARTED_FAILURE"), + + /** + * 停止状态 + */ + STOPPED("STOPPED"), + + /** + * 停止失败状态 + */ + STOPPED_FAILURE("STOPPED_FAILURE"), + + /** + * 被解析状态. 仅仅用于解析插件后被展示的状态 + */ + PARSED("PARSED"); + + + private final String status; + + PluginState(String status) { + this.status = status; + } + + public boolean equals(String status) { + return (this.status.equalsIgnoreCase(status)); + } + + @Override + public String toString() { + return status; + } + + public static PluginState parse(String string) { + for (PluginState status : values()) { + if (status.equals(string)) { + return status; + } + } + return null; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java new file mode 100755 index 0000000..a874db3 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RealizeProvider.java @@ -0,0 +1,66 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.PluginDescriptorLoader; +import com.gitee.starblues.core.scanner.PluginScanner; +import com.gitee.starblues.core.version.VersionInspector; +/** + * 插件扩展配置 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface RealizeProvider { + + /** + * 初始化 + */ + void init(); + + /** + * 当前运行环境 + * @return RuntimeMode + */ + RuntimeMode getRuntimeMode(); + + /** + * 得到 PluginScanner 实现 + * @return PluginScanner + */ + PluginScanner getPluginScanner(); + + /** + * 得到插件基本的检查者 + * @return PluginBasicChecker + */ + PluginBasicChecker getPluginBasicChecker(); + + /** + * 得到 PluginDescriptorLoader 实现 + * @return PluginDescriptorLoader + */ + PluginDescriptorLoader getPluginDescriptorLoader(); + + /** + * 得到 VersionInspector 实现 + * @return VersionInspector + */ + VersionInspector getVersionInspector(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java new file mode 100755 index 0000000..592df1c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/RuntimeMode.java @@ -0,0 +1,60 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core; + +/** + * 插件运行环境 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public enum RuntimeMode { + + /** + * 开发环境 + */ + DEV("dev"), + + /** + * 生产环境 + */ + PROD("prod"); + + private final String mode; + + RuntimeMode(String mode) { + this.mode = mode; + } + + public String getMode() { + return mode; + } + + public static RuntimeMode byName(String model){ + if(DEV.name().equalsIgnoreCase(model)){ + return RuntimeMode.DEV; + } else { + return RuntimeMode.PROD; + } + } + + @Override + public String toString() { + return mode; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java new file mode 100755 index 0000000..925e3b7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginBasicChecker.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.ApplicationContext; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 组合插件检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ComposePluginBasicChecker implements PluginBasicChecker { + + private final List pluginCheckers; + + public ComposePluginBasicChecker(ApplicationContext applicationContext) { + this.pluginCheckers = new ArrayList<>(); + addDefaultChecker(); + addCustomChecker(applicationContext); + } + + protected void addDefaultChecker(){ + pluginCheckers.add(new DefaultPluginBasicChecker()); + } + + protected void addCustomChecker(ApplicationContext applicationContext){ + List pluginCheckers = SpringBeanUtils.getBeans(applicationContext, + PluginBasicChecker.class); + this.pluginCheckers.addAll(pluginCheckers); + } + + public void add(PluginBasicChecker pluginChecker){ + if(pluginChecker != null){ + this.pluginCheckers.add(pluginChecker); + } + } + + + @Override + public void checkPath(Path path) throws Exception { + for (PluginBasicChecker pluginChecker : pluginCheckers) { + pluginChecker.checkPath(path); + } + } + + @Override + public void checkDescriptor(PluginDescriptor descriptor) throws PluginException { + for (PluginBasicChecker pluginChecker : pluginCheckers) { + pluginChecker.checkDescriptor(descriptor); + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java new file mode 100755 index 0000000..feab2ae --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/ComposePluginLauncherChecker.java @@ -0,0 +1,65 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.exception.PluginException; + +import java.util.ArrayList; +import java.util.List; + +/** + * 组合插件检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ComposePluginLauncherChecker implements PluginLauncherChecker { + + private final List pluginCheckers; + + public ComposePluginLauncherChecker() { + this(new ArrayList<>()); + } + + public ComposePluginLauncherChecker(List pluginCheckers) { + this.pluginCheckers = pluginCheckers; + } + + public void add(PluginLauncherChecker pluginChecker){ + if(pluginChecker != null){ + this.pluginCheckers.add(pluginChecker); + } + } + + + @Override + public void checkCanStart(PluginInfo pluginInfo) throws PluginException { + for (PluginLauncherChecker pluginChecker : pluginCheckers) { + pluginChecker.checkCanStart(pluginInfo); + } + } + + @Override + public void checkCanStop(PluginInfo pluginInfo) throws PluginException { + for (PluginLauncherChecker pluginChecker : pluginCheckers) { + pluginChecker.checkCanStop(pluginInfo); + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java new file mode 100755 index 0000000..b63fc6d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginBasicChecker.java @@ -0,0 +1,75 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.Assert; + +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * 默认的基本检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DefaultPluginBasicChecker implements PluginBasicChecker { + + + @Override + public void checkPath(Path path) throws Exception { + if(path == null){ + throw new FileNotFoundException("path 文件路径不能为空"); + } + if(Files.notExists(path)){ + throw new FileNotFoundException("不存在文件: " + path.toString()); + } + } + + @Override + public void checkDescriptor(PluginDescriptor descriptor) throws PluginException { + Assert.isNotNull(descriptor, "PluginDescriptor 不能为空"); + + Assert.isNotEmpty(descriptor.getPluginPath(), "pluginPath 不能为空"); + + Assert.isNotNull(descriptor.getPluginId(), + PluginDescriptorKey.PLUGIN_ID + "不能为空"); + + Assert.isNotNull(descriptor.getPluginBootstrapClass(), + PluginDescriptorKey.PLUGIN_BOOTSTRAP_CLASS + "不能为空"); + + Assert.isNotNull(descriptor.getPluginVersion(), + PluginDescriptorKey.PLUGIN_VERSION + "不能为空"); + + String illegal = PackageStructure.getIllegal(descriptor.getPluginId()); + if(illegal != null){ + throw new PluginException(descriptor, "插件id不能包含:" + illegal); + } + illegal = PackageStructure.getIllegal(descriptor.getPluginVersion()); + if(illegal != null){ + throw new PluginException(descriptor, "插件版本号不能包含:" + illegal); + } + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java new file mode 100755 index 0000000..2c9efde --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DefaultPluginLauncherChecker.java @@ -0,0 +1,99 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.RealizeProvider; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; + +/** + * 默认插件启动检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class DefaultPluginLauncherChecker implements PluginLauncherChecker { + + + protected final RealizeProvider realizeProvider; + protected final IntegrationConfiguration configuration; + + + public DefaultPluginLauncherChecker(RealizeProvider realizeProvider, + IntegrationConfiguration configuration) { + this.realizeProvider = realizeProvider; + this.configuration = configuration; + } + + + @Override + public void checkCanStart(PluginInfo pluginInfo) throws PluginException { + PluginDisabledException.checkDisabled(pluginInfo, configuration, "启动"); + PluginState pluginState = pluginInfo.getPluginState(); + if(pluginState == PluginState.STARTED){ + throw new PluginException(pluginInfo.getPluginDescriptor(), "已经启动, 不能再启动"); + } + checkRequiresVersion(pluginInfo); + } + + + @Override + public void checkCanStop(PluginInfo pluginInfo) throws PluginException { + PluginDisabledException.checkDisabled(pluginInfo, configuration, "停止"); + PluginState pluginState = pluginInfo.getPluginState(); + if(pluginState != PluginState.STARTED){ + throw new PluginException(pluginInfo.getPluginDescriptor(), "没有启动, 不能停止"); + } + } + + /** + * 检查可安装到主程序的版本 + * @param pluginInfo pluginInfo + */ + private void checkRequiresVersion(PluginInfo pluginInfo){ + String version = configuration.version(); + if(ObjectUtils.isEmpty(version) || Constants.ALLOW_VERSION.equals(version)){ + return; + } + String requires = pluginInfo.getPluginDescriptor().getRequires(); + if(ObjectUtils.isEmpty(requires)){ + return; + } + boolean exactVersion = configuration.exactVersion(); + int compareVersion = realizeProvider.getVersionInspector().compareTo(requires, version); + PluginDescriptor descriptor = pluginInfo.getPluginDescriptor(); + if(exactVersion && compareVersion != 0){ + String error = "插件[" + MsgUtils.getPluginUnique(descriptor) + "]" + + "只能安装到[" + requires + "]版本的主程序, 但当前主程序版本为[" + version + "]"; + throw new PluginException(pluginInfo.getPluginDescriptor(), error); + } + if(compareVersion > 0){ + String error = "插件[" + MsgUtils.getPluginUnique(descriptor) + "]" + + "只能安装到小于等于[" + requires + "]版本的主程序, 但当前主程序版本为[" + version + "]"; + throw new PluginException(pluginInfo.getPluginDescriptor(), error); + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java new file mode 100755 index 0000000..a944545 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/DependencyPluginLauncherChecker.java @@ -0,0 +1,126 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.common.DependencyPlugin; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginManager; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.List; +import java.util.Objects; + +/** + * 插件依赖的插件检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DependencyPluginLauncherChecker implements PluginLauncherChecker { + + private final PluginManager pluginManager; + + public DependencyPluginLauncherChecker(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + /** + * 检查插件依赖。如果依赖没有启动, 则自动启动 + * @param pluginInfo 插件信息 + * @throws PluginException PluginException + */ + @Override + public void checkCanStart(PluginInfo pluginInfo) throws PluginException { + List dependencyPlugins = pluginInfo.getPluginDescriptor().getDependencyPlugin(); + if(ObjectUtils.isEmpty(dependencyPlugins)){ + return; + } + PluginDescriptor descriptor = pluginInfo.getPluginDescriptor(); + resolveDependencyPlugin(pluginInfo, (dependencyPlugin, dependencyPluginInfo)->{ + String id = dependencyPlugin.getId(); + String version = dependencyPlugin.getVersion(); + boolean allowAllVersion = Constants.ALLOW_VERSION.equals(version); + + boolean findDependency = false; + if(dependencyPluginInfo != null){ + if(allowAllVersion){ + findDependency = true; + } else { + findDependency = Objects.equals(dependencyPluginInfo.getPluginDescriptor().getPluginVersion(), + descriptor.getPluginVersion()); + } + } + String dependencyPluginUnique = MsgUtils.getPluginUnique(id, allowAllVersion ? null : version); + if(!findDependency){ + throw new PluginException(descriptor, "需要依赖插件[" + dependencyPluginUnique + "]才能启动"); + } + if(dependencyPluginInfo.getPluginState() != PluginState.STARTED){ + // 没有启动的话, 手动启动 + try { + pluginManager.start(dependencyPluginInfo.getPluginId()); + } catch (Exception e){ + if(e instanceof PluginDisabledException){ + // 依赖被禁用, 不能启动 + throw new PluginDisabledException(descriptor, + "依赖的插件[" + dependencyPluginUnique + "]被禁用, 无法启动当前插件"); + } + throw new PluginException(descriptor, + "依赖的插件[" + dependencyPluginUnique + "]启动失败. 无法启动当前插件", e); + } + } + }); + } + + @Override + public void checkCanStop(PluginInfo pluginInfo) throws PluginException { + // 忽略 + } + + private void resolveDependencyPlugin(PluginInfo pluginInfo, ResolveDependencyPlugin resolveDependencyPlugin) + throws PluginException { + List dependencyPlugins = pluginInfo.getPluginDescriptor().getDependencyPlugin(); + if(ObjectUtils.isEmpty(dependencyPlugins)){ + return; + } + for (DependencyPlugin dependencyPlugin : dependencyPlugins) { + if (dependencyPlugin.getOptional()) { + continue; + } + PluginInfo plugin = pluginManager.getPluginInfo(dependencyPlugin.getId()); + resolveDependencyPlugin.process(dependencyPlugin, plugin); + } + } + + @FunctionalInterface + protected interface ResolveDependencyPlugin{ + /** + * 处理依赖的插件 + * @param dependencyPlugin 当前依赖的插件 + * @param dependencyPluginInfo 查询出当前系统安装的依赖的插件信息。可能为空 + * @throws PluginException 处理异常 + */ + void process(DependencyPlugin dependencyPlugin, PluginInfo dependencyPluginInfo) throws PluginException; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java new file mode 100755 index 0000000..6975610 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginBasicChecker.java @@ -0,0 +1,47 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; + +/** + * 插件基本检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginBasicChecker { + + /** + * 根据路径检查 + * @param path path + * @throws Exception 检查异常 + */ + void checkPath(Path path) throws Exception; + + /** + * 检查插件描述是否合法 + * @param descriptor 插件信息 + * @throws PluginException 检查异常 + */ + void checkDescriptor(PluginDescriptor descriptor) throws PluginException; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java new file mode 100755 index 0000000..14f01c5 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/checker/PluginLauncherChecker.java @@ -0,0 +1,46 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.checker; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.exception.PluginException; + +/** + * 插件启动检查者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginLauncherChecker { + + /** + * 检查是否能启动 + * @param pluginInfo 插件信息 + * @throws PluginException 不能启动, 抛出PluginException异常即可 + */ + void checkCanStart(PluginInfo pluginInfo) throws PluginException; + + /** + * 检查是否能停止 + * @param pluginInfo 插件信息 + * @throws PluginException 不能停止, 抛出PluginException异常即可 + */ + void checkCanStop(PluginInfo pluginInfo) throws PluginException; + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java new file mode 100755 index 0000000..41edbd9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/CacheMainResourceMatcher.java @@ -0,0 +1,55 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可缓存的 ResourceMatcher + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class CacheMainResourceMatcher extends DefaultMainResourceMatcher implements AutoCloseable { + + public static final String TYPE = "CacheMainResourceMatcher"; + + private final Map resourceUrlMatchCache = new ConcurrentHashMap<>(); + + public CacheMainResourceMatcher(MainResourcePatternDefiner mainResourcePatternDefiner) { + super(mainResourcePatternDefiner); + } + + @Override + public Boolean match(String resourceUrl) { + Boolean match = resourceUrlMatchCache.get(resourceUrl); + if(match == null){ + match = super.match(resourceUrl); + resourceUrlMatchCache.put(resourceUrl, match); + } + return match; + } + + + @Override + public void close() throws Exception { + resourceUrlMatchCache.clear(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ComposeMainResourceMatcher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ComposeMainResourceMatcher.java new file mode 100755 index 0000000..ee30103 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ComposeMainResourceMatcher.java @@ -0,0 +1,73 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 组合的 MainResourcePatternDefiner + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class ComposeMainResourceMatcher implements MainResourceMatcher, AutoCloseable{ + + private final List resourceMatchers; + + public ComposeMainResourceMatcher(){ + this(null); + } + + public ComposeMainResourceMatcher(List resourceMatchers) { + if(ObjectUtils.isEmpty(resourceMatchers)){ + this.resourceMatchers = new ArrayList<>(); + } else { + this.resourceMatchers = resourceMatchers; + } + } + + public void addMainResourceMatcher(MainResourceMatcher mainResourceMatcher){ + if(mainResourceMatcher == null){ + return; + } + resourceMatchers.add(mainResourceMatcher); + } + + @Override + public Boolean match(String resourceUrl) { + for (MainResourceMatcher resourceMatcher : resourceMatchers) { + if(resourceMatcher.match(resourceUrl)){ + return Boolean.TRUE; + } + } + return Boolean.FALSE; + } + + @Override + public void close() throws Exception { + for (MainResourceMatcher resourceMatcher : resourceMatchers) { + if(resourceMatcher instanceof AutoCloseable){ + IOUtils.closeQuietly((AutoCloseable)resourceMatcher); + } + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java new file mode 100755 index 0000000..11f206f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/DefaultMainResourceMatcher.java @@ -0,0 +1,82 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.UrlUtils; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.util.Collection; +import java.util.Set; + +/** + * 默认的主程序资源匹配者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class DefaultMainResourceMatcher implements MainResourceMatcher{ + + private final Set includePatterns; + private final Set excludePatterns; + + private final PathMatcher pathMatcher; + + public DefaultMainResourceMatcher(MainResourcePatternDefiner mainResourcePatternDefiner) { + this.includePatterns = mainResourcePatternDefiner.getIncludePatterns(); + this.excludePatterns = mainResourcePatternDefiner.getExcludePatterns(); + this.pathMatcher = new AntPathMatcher(); + } + + @Override + public Boolean match(String resourceUrl) { + return match(includePatterns, resourceUrl); + } + + private Boolean match(Collection patterns, String url){ + if(ObjectUtils.isEmpty(patterns) || ObjectUtils.isEmpty(url)){ + return Boolean.FALSE; + } + url = UrlUtils.formatMatchUrl(url); + for (String pattern : patterns) { + boolean match = pathMatcher.match(pattern, url); + if(match){ + return !excludeMatch(excludePatterns, url); + } + } + return Boolean.FALSE; + } + + private Boolean excludeMatch(Collection patterns, String url){ + if(ObjectUtils.isEmpty(patterns) || ObjectUtils.isEmpty(url)){ + return Boolean.FALSE; + } + url = UrlUtils.formatMatchUrl(url); + for (String pattern : patterns) { + boolean match = pathMatcher.match(pattern, url); + if(match){ + return Boolean.TRUE; + } + } + return Boolean.FALSE; + } + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java new file mode 100755 index 0000000..3024418 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/EmptyMainResourcePatternDefiner.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import java.util.Set; + +/** + * 空的 MainResourceDefiner + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class EmptyMainResourcePatternDefiner implements MainResourcePatternDefiner { + + @Override + public Set getIncludePatterns() { + return null; + } + + @Override + public Set getExcludePatterns() { + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java new file mode 100755 index 0000000..059f749 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourceMatcher.java @@ -0,0 +1,35 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +/** + * 主程序资源匹配者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public interface MainResourceMatcher { + + /** + * 匹配主程序资源 + * @param resourceUrl 主程序资源url + * @return true 匹配成功, false 匹配失败 + */ + Boolean match(String resourceUrl); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java new file mode 100755 index 0000000..b8fb11d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/MainResourcePatternDefiner.java @@ -0,0 +1,42 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import java.util.Set; + +/** + * 主程序定义者, 从主程序加载资源的定义者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface MainResourcePatternDefiner { + + /** + * 包含资源名称. + * @return 资源名称集合 + */ + Set getIncludePatterns(); + + /** + * 排除资源 + * @return String + */ + Set getExcludePatterns(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java new file mode 100755 index 0000000..d7c770b --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/NestedPluginJarResourceLoader.java @@ -0,0 +1,122 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + + +import com.gitee.starblues.core.descriptor.PluginLibInfo; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.loader.classloader.*; +import com.gitee.starblues.loader.classloader.resource.loader.*; +import com.gitee.starblues.loader.classloader.resource.storage.ResourceStorage; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.extern.slf4j.Slf4j; + +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; + +/** + * 嵌套插件jar加载者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +@Slf4j +public class NestedPluginJarResourceLoader extends AbstractResourceLoader { + + private final InsidePluginDescriptor pluginDescriptor; + private final ResourceLoaderFactory resourceLoaderFactory; + private final ResourceLoaderFactory parentResourceLoaderFactory; + + public NestedPluginJarResourceLoader(InsidePluginDescriptor pluginDescriptor, + ResourceLoaderFactory resourceLoaderFactory) throws Exception { + this(pluginDescriptor, resourceLoaderFactory, null); + } + + public NestedPluginJarResourceLoader(InsidePluginDescriptor pluginDescriptor, + ResourceLoaderFactory resourceLoaderFactory, + ResourceLoaderFactory parentResourceLoaderFactory) throws Exception { + super(new URL("jar:" + pluginDescriptor.getInsidePluginPath().toUri().toURL() + "!/")); + this.pluginDescriptor = pluginDescriptor; + this.resourceLoaderFactory = resourceLoaderFactory; + this.parentResourceLoaderFactory = parentResourceLoaderFactory; + } + + @Override + protected void loadOfChild(ResourceStorage resourceStorage) throws Exception { + try (JarFile jarFile = new JarFile(pluginDescriptor.getInsidePluginPath().toFile())) { + addClassPath(resourceStorage, jarFile); + addLib(jarFile); + } + } + + private void addClassPath(ResourceStorage resourceStorage, JarFile jarFile) throws Exception{ + String classesPath = pluginDescriptor.getPluginClassPath(); + Enumeration entries = jarFile.entries(); + resourceLoaderFactory.addResource(new BaseURLResourceLoader(baseUrl)); + resourceLoaderFactory.addResource(new BaseURLResourceLoader(new URL(baseUrl, "classes/"))); + while (entries.hasMoreElements()){ + JarEntry jarEntry = entries.nextElement(); + if(!jarEntry.getName().startsWith(classesPath)){ + continue; + } + String realName = jarEntry.getName().replace(classesPath, ""); + URL url = new URL(baseUrl.toString() + jarEntry.getName()); + resourceLoaderFactory.addResource(new DefaultResource(realName, baseUrl, url)); + resourceStorage.add(new CacheResource(realName, baseUrl, url, ()->{ + return getClassBytes(realName, jarFile.getInputStream(jarEntry), true); + })); + } + } + + private void addLib(JarFile jarFile) throws Exception { + JarEntry jarEntry = null; + Set pluginLibInfos = pluginDescriptor.getPluginLibInfo(); + String pluginUnique = MsgUtils.getPluginUnique(pluginDescriptor); + for (PluginLibInfo pluginLibInfo : pluginLibInfos) { + String entryName = pluginLibInfo.getPath(); + jarEntry = jarFile.getJarEntry(entryName); + if(jarEntry == null){ + log.debug("Not found: " + pluginLibInfo.getPath()); + continue; + } + if (jarEntry.getMethod() != ZipEntry.STORED) { + throw new PluginException("插件依赖压缩方式错误, 必须是: 存储(stored)压缩方式"); + } + InputStream jarFileInputStream = jarFile.getInputStream(jarEntry); + URL url = new URL(baseUrl.toString() + pluginLibInfo.getPath() + "!/"); + if(parentResourceLoaderFactory != null && pluginLibInfo.isLoadToMain()){ + parentResourceLoaderFactory.addResource(new JarResourceLoader(url, new JarInputStream(jarFileInputStream))); + log.debug("插件[{}]依赖被加载到主程序中: {}", pluginUnique, pluginLibInfo.getPath()); + } else { + JarResourceLoader jarResourceLoader = new JarResourceLoader(url, new JarInputStream(jarFileInputStream)); + resourceLoaderFactory.addResource(jarResourceLoader); + log.debug("插件[{}]依赖被加载: {}", pluginUnique, pluginLibInfo.getPath()); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java new file mode 100755 index 0000000..116900a --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginClassLoader.java @@ -0,0 +1,119 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginLibInfo; +import com.gitee.starblues.core.descriptor.PluginType; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.loader.classloader.*; +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.utils.IOUtils; +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Set; + +/** + * 插件 classLoader + * + * @author starBlues + * @version 3.0.3 + * @since 3.0.0 + */ +@Slf4j +public class PluginClassLoader extends GenericClassLoader implements PluginResourceLoaderFactory{ + + private final MainResourceMatcher mainResourceMatcher; + + private final PluginResourceLoaderFactory proxy; + + public PluginClassLoader(String name, GenericClassLoader parentClassLoader, + ClassLoader classLoader, + ResourceLoaderFactory resourceLoaderFactory, + MainResourceMatcher mainResourceMatcher) { + super(name, classLoader, resourceLoaderFactory); + this.mainResourceMatcher = mainResourceMatcher; + this.proxy = new PluginResourceLoaderFactoryProxy(resourceLoaderFactory, parentClassLoader); + } + + @Override + public void addResource(InsidePluginDescriptor descriptor) throws Exception { + proxy.addResource(descriptor); + } + + @Override + protected Class findClassFromParent(String className) throws ClassNotFoundException { + if(mainResourceMatcher.match(className.replace(".", "/"))){ + try { + return super.findClassFromParent(className); + } catch (Exception e){ + // 忽略 + } + } + return null; + } + + @Override + protected InputStream findInputStreamFromParent(String name) { + if(mainResourceMatcher.match(name)){ + try { + return super.findInputStreamFromParent(name); + } catch (Exception e){ + // 忽略 + } + } + return null; + } + + @Override + protected URL findResourceFromParent(String name) { + if(mainResourceMatcher.match(name)){ + return super.findResourceFromParent(name); + } + return null; + } + + @Override + protected Enumeration findResourcesFromParent(String name) throws IOException { + if(mainResourceMatcher.match(name)){ + return super.findResourcesFromParent(name); + } + return null; + } + + @Override + public void close() throws IOException { + super.close(); + if(mainResourceMatcher instanceof AutoCloseable){ + try { + ((AutoCloseable) mainResourceMatcher).close(); + } catch (Exception e){ + throw new IOException(e); + } + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginGeneralUrlClassLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginGeneralUrlClassLoader.java new file mode 100755 index 0000000..11e5259 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginGeneralUrlClassLoader.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.loader.classloader.GeneralUrlClassLoader; +import com.gitee.starblues.loader.utils.IOUtils; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +/** + * 插件基本 url classLoader + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +@Slf4j +public class PluginGeneralUrlClassLoader extends GeneralUrlClassLoader implements PluginResourceLoaderFactory{ + + private final PluginResourceLoaderFactory proxy; + + public PluginGeneralUrlClassLoader(String name, GeneralUrlClassLoader parent) { + super(name, parent); + this.proxy = new PluginResourceLoaderFactoryProxy(this, parent); + } + + @Override + public void addResource(InsidePluginDescriptor descriptor) throws Exception { + proxy.addResource(descriptor); + } + + @Override + public void close() throws IOException { + super.close(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactory.java new file mode 100755 index 0000000..8156313 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactory.java @@ -0,0 +1,40 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; + +/** + * 插件资源工程 + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public interface PluginResourceLoaderFactory extends ResourceLoaderFactory { + + /** + * 加载插件资源 + * @param descriptor 插件资源描述 + * @throws Exception 添加插件资源异常 + * @since 3.0.4 + */ + void addResource(InsidePluginDescriptor descriptor) throws Exception; + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactoryProxy.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactoryProxy.java new file mode 100755 index 0000000..594b738 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/PluginResourceLoaderFactoryProxy.java @@ -0,0 +1,187 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginLibInfo; +import com.gitee.starblues.core.descriptor.PluginType; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.loader.classloader.resource.Resource; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoader; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; + +/** + * 插件资源加载工厂代理 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.1 + */ +@Slf4j +public class PluginResourceLoaderFactoryProxy implements PluginResourceLoaderFactory { + + private final ResourceLoaderFactory target; + private final ResourceLoaderFactory parent; + + public PluginResourceLoaderFactoryProxy(ResourceLoaderFactory target) { + this(target, null); + } + + public PluginResourceLoaderFactoryProxy(ResourceLoaderFactory target, ResourceLoaderFactory parent) { + this.target = target; + this.parent = parent; + } + + @Override + public void addResource(InsidePluginDescriptor descriptor) throws Exception { + PluginType pluginType = descriptor.getType(); + if(PluginType.isNestedPackage(pluginType)){ + NestedPluginJarResourceLoader resourceLoader = + new NestedPluginJarResourceLoader(descriptor, target, parent); + target.addResource(resourceLoader); + } else if(PluginType.isOuterPackage(pluginType)){ + addOuterPluginClasspath(descriptor); + addLibFile(descriptor); + } else { + addDirPluginClasspath(descriptor); + addLibFile(descriptor); + } + } + + private void addOuterPluginClasspath(InsidePluginDescriptor descriptor) throws Exception{ + String pluginPath = descriptor.getPluginPath(); + File existFile = FilesUtils.getExistFile(pluginPath); + if(existFile != null){ + addResource(existFile); + log.debug("插件[{}]Classpath已被加载: {}", MsgUtils.getPluginUnique(descriptor), existFile.getPath()); + } else { + throw new PluginException("没有发现插件路径: " + pluginPath); + } + } + + private void addDirPluginClasspath(InsidePluginDescriptor descriptor) throws Exception { + String pluginClassPath = descriptor.getPluginClassPath(); + File existFile = FilesUtils.getExistFile(pluginClassPath); + if(existFile != null){ + addResource(existFile); + log.debug("插件[{}]Classpath已被加载: {}", MsgUtils.getPluginUnique(descriptor), existFile.getPath()); + } + } + + + private void addLibFile(InsidePluginDescriptor pluginDescriptor) throws Exception { + Set pluginLibInfos = pluginDescriptor.getPluginLibInfo(); + if(ObjectUtils.isEmpty(pluginLibInfos)){ + return; + } + String pluginUnique = MsgUtils.getPluginUnique(pluginDescriptor); + String pluginLibDir = pluginDescriptor.getPluginLibDir(); + if(!ObjectUtils.isEmpty(pluginLibDir)){ + log.info("插件[{}]依赖加载目录: {}", pluginUnique, pluginLibDir); + } + if(pluginLibInfos.isEmpty()){ + log.warn("插件[{}]依赖为空!", pluginUnique); + return; + } + for (PluginLibInfo pluginLibInfo : pluginLibInfos) { + File existFile = FilesUtils.getExistFile(pluginLibInfo.getPath()); + if(existFile != null){ + if(pluginLibInfo.isLoadToMain()){ + // 加载到主程序中 + parent.addResource(existFile); + log.debug("插件[{}]依赖被加载到主程序中: {}", pluginUnique, existFile.getPath()); + } else { + target.addResource(existFile); + log.debug("插件[{}]依赖被加载: {}", pluginUnique, existFile.getPath()); + } + } + } + } + + + @Override + public void addResource(String path) throws Exception { + target.addResource(path); + } + + @Override + public void addResource(File file) throws Exception { + target.addResource(file); + } + + @Override + public void addResource(Path path) throws Exception { + target.addResource(path); + } + + @Override + public void addResource(URL url) throws Exception { + target.addResource(url); + } + + @Override + public void addResource(Resource resource) throws Exception { + target.addResource(resource); + } + + @Override + public void addResource(ResourceLoader resourceLoader) throws Exception { + target.addResource(resourceLoader); + } + + @Override + public Resource findFirstResource(String name) { + return target.findFirstResource(name); + } + + @Override + public Enumeration findAllResource(String name) { + return target.findAllResource(name); + } + + @Override + public InputStream getInputStream(String name) { + return target.getInputStream(name); + } + + @Override + public List getUrls() { + return target.getUrls(); + } + + @Override + public void close() throws Exception { + target.close(); + } + + @Override + public void release() throws Exception { + target.release(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java new file mode 100755 index 0000000..3ba3964 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/classloader/ProhibitMainResourceMatcher.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.classloader; + +/** + * 禁止匹配所有主程序资源 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class ProhibitMainResourceMatcher implements MainResourceMatcher{ + + @Override + public Boolean match(String resourceUrl) { + return false; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java new file mode 100755 index 0000000..90f2164 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/AbstractPluginDescriptorLoader.java @@ -0,0 +1,247 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + + +import com.gitee.starblues.common.AbstractDependencyPlugin; +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.common.DependencyPlugin; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import com.gitee.starblues.core.exception.PluginDecryptException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import static com.gitee.starblues.common.PluginDescriptorKey.*; +import static com.gitee.starblues.utils.PropertiesUtils.getValue; + +/** + * 抽象的 PluginDescriptorLoader + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +@Slf4j +public abstract class AbstractPluginDescriptorLoader implements PluginDescriptorLoader{ + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + protected final PluginDescriptorDecrypt pluginDescriptorDecrypt; + + protected AbstractPluginDescriptorLoader(PluginDescriptorDecrypt pluginDescriptorDecrypt) { + this.pluginDescriptorDecrypt = pluginDescriptorDecrypt; + } + + @Override + public InsidePluginDescriptor load(Path location) throws PluginException { + PluginMeta pluginMeta = null; + try { + pluginMeta = getPluginMetaInfo(location); + if(pluginMeta == null || pluginMeta.getProperties() == null){ + logger.debug("路径[{}]没有发现插件配置信息", location); + return null; + } + return create(pluginMeta, location); + } catch (Throwable e) { + if(e instanceof PluginException){ + throw (PluginException) e; + } else { + throw new PluginException(e.getMessage(), e); + } + } + } + + @Override + public void close() throws Exception { + + } + + /** + * 子类获取插件信息 + * @param location 路径 + * @return Properties + * @throws Exception 异常 + */ + protected abstract PluginMeta getPluginMetaInfo(Path location) throws Exception; + + protected DefaultInsidePluginDescriptor create(PluginMeta pluginMeta, Path path) throws Exception{ + Properties properties = pluginMeta.getProperties(); + DefaultInsidePluginDescriptor descriptor = new DefaultInsidePluginDescriptor( + getValue(properties, PLUGIN_ID), + getValue(properties, PLUGIN_VERSION), + getValue(properties, PLUGIN_BOOTSTRAP_CLASS), + path + ); + descriptor.setType(PluginType.byName(pluginMeta.getPackageType())); + + PluginResourcesConfig resourcesConfig = getPluginResourcesConfig(path, properties); + + String pluginLibDir = getValue(properties, PLUGIN_LIB_DIR, false); + descriptor.setPluginLibDir(pluginLibDir); + descriptor.setPluginLibInfo(getPluginLibInfo(descriptor, resourcesConfig.getDependenciesIndex())); + descriptor.setIncludeMainResourcePatterns(resourcesConfig.getLoadMainResourceIncludes()); + descriptor.setExcludeMainResourcePatterns(resourcesConfig.getLoadMainResourceExcludes()); + + descriptor.setProperties(properties); + descriptor.setPluginClassPath(getValue(properties, PLUGIN_PATH, false)); + descriptor.setDescription(getValue(properties, PLUGIN_DESCRIPTION, false)); + descriptor.setRequires(getValue(properties, PLUGIN_REQUIRES, false)); + descriptor.setProvider(getValue(properties, PLUGIN_PROVIDER, false)); + descriptor.setLicense(getValue(properties, PLUGIN_LICENSE, false)); + descriptor.setConfigFileName(getValue(properties, PLUGIN_CONFIG_FILE_NAME, false)); + descriptor.setConfigFileLocation(getValue(properties, PLUGIN_CONFIG_FILE_LOCATION, false)); + descriptor.setArgs(getValue(properties, PLUGIN_ARGS, false)); + + descriptor.setDependencyPlugins(getPluginDependency(properties)); + return descriptor; + } + + + protected List getPluginDependency(Properties properties){ + return AbstractDependencyPlugin.toList(getValue(properties, PLUGIN_DEPENDENCIES, false), + DefaultDependencyPlugin::new); + } + + protected PluginResourcesConfig getPluginResourcesConfig(Path path, Properties properties) throws Exception{ + String libIndex = getValue(properties, PLUGIN_RESOURCES_CONFIG); + if(ObjectUtils.isEmpty(libIndex)){ + return new PluginResourcesConfig(); + } + File file = new File(libIndex); + if(!file.exists()){ + // 如果绝对路径不存在, 则判断相对路径 + String pluginPath = getValue(properties, PLUGIN_PATH); + file = new File(FilesUtils.joiningFilePath(pluginPath, libIndex)); + } + if(!file.exists()){ + // 都不存在, 则返回为空 + return new PluginResourcesConfig(); + } + try { + List lines = Files.readAllLines(file.toPath()); + return PluginResourcesConfig.parse(lines); + } catch (IOException e) { + throw new Exception("Load plugin lib index path failure. " + libIndex, e); + } + } + + protected Set getPluginLibInfo(DefaultInsidePluginDescriptor descriptor, Set dependenciesIndex){ + String configPluginLibDir = descriptor.getPluginLibDir(); + boolean isConfigPluginLibDir = false; + if(!ObjectUtils.isEmpty(configPluginLibDir)){ + String libDir = getLibDir(descriptor, configPluginLibDir); + if(!ObjectUtils.isEmpty(libDir)){ + descriptor.setPluginLibDir(libDir); + isConfigPluginLibDir = true; + } + } + if(ObjectUtils.isEmpty(dependenciesIndex)){ + return Collections.emptySet(); + } + Set pluginLibInfos = new HashSet<>(dependenciesIndex.size()); + for (String index : dependenciesIndex) { + boolean loadToMain; + if(index.endsWith(Constants.LOAD_TO_MAIN_SIGN)){ + index = index.substring(0, index.lastIndexOf(Constants.LOAD_TO_MAIN_SIGN)); + loadToMain = true; + } else { + loadToMain = false; + } + String libPath = index; + if(isConfigPluginLibDir){ + libPath = getLibPath(descriptor, configPluginLibDir, index); + } + pluginLibInfos.add(new PluginLibInfo(libPath, loadToMain)); + } + return pluginLibInfos; + } + + protected String getLibDir(DefaultInsidePluginDescriptor descriptor, String configPluginLibDir){ + if(FilesUtils.existFile(configPluginLibDir)){ + return configPluginLibDir; + } + // 先检查插件相对目录 + File libDirFile = FilesUtils.resolveExistRelativePathFile(descriptor.getPluginPath(), configPluginLibDir); + if(libDirFile != null){ + return libDirFile.getPath(); + } + // 再相对插件存放目录 + libDirFile = FilesUtils.resolveExistRelativePathFile( + new File(descriptor.getPluginPath()).getParent(), configPluginLibDir); + if(libDirFile != null){ + return libDirFile.getPath(); + } + // 最后相对主程序目录 + libDirFile = FilesUtils.resolveExistRelativePathFile(new File("").getAbsolutePath(), + configPluginLibDir); + if(libDirFile != null){ + return libDirFile.getPath(); + } + return null; + } + + protected String getLibPath(DefaultInsidePluginDescriptor descriptor, String configPluginLibDir, String index){ + String pluginLibDir = descriptor.getPluginLibDir(); + if(ObjectUtils.isEmpty(pluginLibDir)){ + return index; + } + String joiningFilePath = FilesUtils.joiningFilePath(descriptor.getPluginLibDir(), index); + if(index.startsWith(configPluginLibDir)){ + // 如果 index 中前缀配置了 PLUGIN.META 中的 plugin.libDir 则尝试判断完整拼接的依赖路径文件是否存在 + // 如果存在, 则返回, 如果不存在, 则去掉重复前缀, 返回。该处是为了兼容解压后的jar中index存在 libDir 前缀 + if(FilesUtils.existFile(joiningFilePath)){ + return joiningFilePath; + } + return FilesUtils.joiningFilePath(descriptor.getPluginLibDir(), + index.replace(configPluginLibDir, "")); + } + return joiningFilePath; + } + + protected Properties getDecryptProperties(InputStream inputStream) throws Exception{ + Properties properties = new Properties(); + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);){ + properties.load(reader); + } + String pluginId = getValue(properties, PLUGIN_ID); + return pluginDescriptorDecrypt.decrypt(pluginId, properties); + } + + @AllArgsConstructor + @Getter + public static class PluginMeta{ + private final String packageType; + private final Properties properties; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java new file mode 100755 index 0000000..6bcc236 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ComposeDescriptorLoader.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.core.checker.PluginBasicChecker; +import com.gitee.starblues.core.descriptor.decrypt.EmptyPluginDescriptorDecrypt; +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.SpringBeanUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 组合插件描述加载者 + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +@Slf4j +public class ComposeDescriptorLoader implements PluginDescriptorLoader{ + + private final List pluginDescriptorLoaders = new ArrayList<>(); + + private final ApplicationContext applicationContext; + private final PluginBasicChecker pluginChecker; + + + public ComposeDescriptorLoader(ApplicationContext applicationContext, PluginBasicChecker pluginChecker) { + this.applicationContext = applicationContext; + this.pluginChecker = pluginChecker; + addDefaultLoader(); + } + + protected void addDefaultLoader(){ + PluginDescriptorDecrypt pluginDescriptorDecrypt = getPluginDescriptorDecrypt(applicationContext); + addLoader(new DevPluginDescriptorLoader(pluginDescriptorDecrypt)); + addLoader(new ProdPluginDescriptorLoader(pluginDescriptorDecrypt)); + } + + protected PluginDescriptorDecrypt getPluginDescriptorDecrypt(ApplicationContext applicationContext){ + PluginDescriptorDecrypt pluginDescriptorDecrypt = + SpringBeanUtils.getExistBean(applicationContext, PluginDescriptorDecrypt.class); + if(pluginDescriptorDecrypt != null){ + return pluginDescriptorDecrypt; + } else { + return new EmptyPluginDescriptorDecrypt(); + } + } + + public void addLoader(PluginDescriptorLoader descriptorLoader){ + if(descriptorLoader != null){ + pluginDescriptorLoaders.add(descriptorLoader); + } + } + + + @Override + public InsidePluginDescriptor load(Path location) throws PluginException { + for (PluginDescriptorLoader pluginDescriptorLoader : pluginDescriptorLoaders) { + try { + InsidePluginDescriptor pluginDescriptor = pluginDescriptorLoader.load(location); + if(pluginDescriptor != null){ + pluginChecker.checkDescriptor(pluginDescriptor); + return pluginDescriptor; + } + } catch (Exception e){ + log.debug("非法路径插件: {}", location); + } + } + return null; + } + + @Override + public void close() throws Exception { + + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java new file mode 100755 index 0000000..1ee1201 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultDependencyPlugin.java @@ -0,0 +1,67 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.AbstractDependencyPlugin; + +/** + * 依赖的插件信息 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DefaultDependencyPlugin extends AbstractDependencyPlugin { + + private String id; + private String version; + private Boolean optional = false; + + @Override + public String getId() { + return id; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public Boolean getOptional() { + if(optional == null){ + return false; + } + return optional; + } + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public void setVersion(String version) { + this.version = version; + } + + @Override + public void setOptional(Boolean optional) { + if(optional == null){ + optional = false; + } + this.optional = optional; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java new file mode 100755 index 0000000..93c93f2 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultInsidePluginDescriptor.java @@ -0,0 +1,136 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import lombok.Setter; + +import java.nio.file.Path; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Manifest; + +/** + * 内部的默认插件描述者 + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public class DefaultInsidePluginDescriptor extends DefaultPluginDescriptor implements InsidePluginDescriptor { + + private final Path pluginPath; + private final String pluginFileName; + + @Setter + private String pluginClassPath; + @Setter + private Properties properties; + @Setter + private String configFileName; + @Setter + private String configFileLocation; + @Setter + private String args; + @Setter + private String pluginLibDir; + @Setter + private Set pluginLibInfo; + @Setter + private Set includeMainResourcePatterns; + @Setter + private Set excludeMainResourcePatterns; + + public DefaultInsidePluginDescriptor(String pluginId, String pluginVersion, String pluginClass, Path pluginPath) { + super(pluginId, pluginVersion, pluginClass, pluginPath.toAbsolutePath().toString()); + this.pluginPath = pluginPath; + this.pluginFileName = pluginPath.toFile().getName(); + } + + + @Override + public String getPluginClassPath() { + return pluginClassPath; + } + + @Override + public String getPluginLibDir() { + return pluginLibDir; + } + + @Override + public Set getPluginLibInfo() { + return pluginLibInfo; + } + + @Override + public Set getIncludeMainResourcePatterns() { + return includeMainResourcePatterns; + } + + @Override + public Set getExcludeMainResourcePatterns() { + return excludeMainResourcePatterns; + } + + @Override + public String getConfigFileName() { + return configFileName; + } + + @Override + public String getConfigFileLocation() { + return configFileLocation; + } + + @Override + public String getArgs() { + return args; + } + + @Override + public Path getInsidePluginPath() { + return pluginPath; + } + + @Override + public String getPluginFileName() { + return pluginFileName; + } + + @Override + public Properties getProperties() { + return properties; + } + + @Override + public PluginDescriptor toPluginDescriptor() { + Path pluginPath = getInsidePluginPath(); + if(getType() == PluginType.DEV) { + // dev模式 插件路径展示项目目录 + pluginPath = pluginPath.getParent().getParent(); + } + DefaultPluginDescriptor descriptor = new DefaultPluginDescriptor( + getPluginId(), getPluginVersion(), getPluginBootstrapClass(), pluginPath.toAbsolutePath().toString() + ); + descriptor.setType(getType()); + descriptor.setDescription(getDescription()); + descriptor.setProvider(getProvider()); + descriptor.setRequires(getRequires()); + descriptor.setLicense(getLicense()); + return descriptor; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java new file mode 100755 index 0000000..8e2e330 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DefaultPluginDescriptor.java @@ -0,0 +1,146 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.DependencyPlugin; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.Assert; + +import java.util.List; + +import static com.gitee.starblues.common.PluginDescriptorKey.*; + +/** + * 默认插件描述者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DefaultPluginDescriptor implements PluginDescriptor { + + private final String pluginId; + private final String pluginVersion; + private final String pluginBootstrapClass; + private final String pluginPath; + + + private PluginType type; + private String description; + private String requires; + private String provider; + private String license; + + private List dependencyPlugins; + + public DefaultPluginDescriptor(String pluginId, String pluginVersion, + String pluginClass, String pluginPath) { + this.pluginId = Assert.isNotEmpty(pluginId, PLUGIN_ID + "不能为空"); + this.pluginVersion = Assert.isNotEmpty(pluginVersion, PLUGIN_VERSION + "不能为空"); + this.pluginBootstrapClass = Assert.isNotEmpty(pluginClass, PLUGIN_BOOTSTRAP_CLASS + "不能为空"); + this.pluginPath = Assert.isNotNull(pluginPath, "插件路径[pluginPath]不能为空"); + check(); + } + + void setDescription(String description) { + this.description = description; + } + + void setRequires(String requires) { + this.requires = requires; + } + + void setProvider(String provider) { + this.provider = provider; + } + + void setLicense(String license) { + this.license = license; + } + + void setType(PluginType type) { + this.type = type; + } + + void setDependencyPlugins(List dependencyPlugins) { + this.dependencyPlugins = dependencyPlugins; + } + + @Override + public String getPluginId() { + return pluginId; + } + + @Override + public String getPluginVersion() { + return pluginVersion; + } + + @Override + public String getPluginBootstrapClass() { + return pluginBootstrapClass; + } + + @Override + public String getPluginPath() { + return pluginPath; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getRequires() { + return requires; + } + + @Override + public String getProvider() { + return provider; + } + + @Override + public String getLicense() { + return license; + } + + @Override + public List getDependencyPlugin() { + return dependencyPlugins; + } + + + @Override + public PluginType getType() { + return type; + } + + private void check(){ + String illegal = PackageStructure.getIllegal(pluginId); + if(illegal != null){ + throw new PluginException(this, "插件id不能包含:" + illegal); + } + illegal = PackageStructure.getIllegal(pluginVersion); + if(illegal != null){ + throw new PluginException(this, "插件版本号不能包含:" + illegal); + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java new file mode 100755 index 0000000..2b9b29c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/DevPluginDescriptorLoader.java @@ -0,0 +1,74 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +/** + * 开发环境 PluginDescriptorLoader 加载者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +@Slf4j +public class DevPluginDescriptorLoader extends AbstractPluginDescriptorLoader{ + + public DevPluginDescriptorLoader(PluginDescriptorDecrypt pluginDescriptorDecrypt) { + super(pluginDescriptorDecrypt); + } + + @Override + protected PluginMeta getPluginMetaInfo(Path location) throws Exception { + String pluginMetaPath = location.toString() + File.separator + PackageStructure.PLUGIN_META_NAME; + File file = new File(pluginMetaPath); + if(!file.exists()){ + log.debug("Path: [{}] not exist.", location); + return null; + } + Path path = Paths.get(pluginMetaPath); + Properties properties = super.getDecryptProperties(Files.newInputStream(path)); + if(properties == null || properties.isEmpty()){ + log.debug("Load plugin properties is empty from '{}'", path); + return null; + } + return new PluginMeta(PackageType.PLUGIN_PACKAGE_TYPE_DEV, properties); + } + + @Override + protected String getLibPath(DefaultInsidePluginDescriptor descriptor, String configPluginLibDir, String index) { + return index; + } + + @Override + protected DefaultInsidePluginDescriptor create(PluginMeta pluginMeta, Path path) throws Exception { + final DefaultInsidePluginDescriptor descriptor = super.create(pluginMeta, path); + descriptor.setType(PluginType.DEV); + return descriptor; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java new file mode 100755 index 0000000..472125e --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/InsidePluginDescriptor.java @@ -0,0 +1,108 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import java.nio.file.Path; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Manifest; + +/** + * 内部的PluginDescriptor + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface InsidePluginDescriptor extends PluginDescriptor{ + + /** + * 得到插件的 Properties 配置 + * @return Properties + */ + Properties getProperties(); + + /** + * 获取插件配置文件名称。 + * 和 getConfigFileLocation 配置二选一, 如果都有值则默认使用 getConfigFileName + * @return String + */ + String getConfigFileName(); + + /** + * 获取插件配置文件路径。 + * 和 getConfigFileName 配置二选一, 如果都有值则默认使用 getConfigFileName + * @return String + */ + String getConfigFileLocation(); + + /** + * 得到插件启动时参数 + * @return String + */ + String getArgs(); + + /** + * 得到内部的插件路径 + * @return Path + */ + Path getInsidePluginPath(); + + /** + * 获取插件文件名称 + * @return String + */ + String getPluginFileName(); + + + /** + * 获取插件classes path路径 + * @return Path + */ + String getPluginClassPath(); + + /** + * 获取插件依赖配置的目录 + * @return String + */ + String getPluginLibDir(); + + /** + * 获取插件依赖的路径 + * @return String + */ + Set getPluginLibInfo(); + + /** + * 设置当前插件包含主程序加载资源的匹配 + * @return Set + */ + Set getIncludeMainResourcePatterns(); + + /** + * 设置当前插件排除从主程序加载资源的匹配 + * @return Set + */ + Set getExcludeMainResourcePatterns(); + + /** + * 转换为 PluginDescriptor + * @return PluginDescriptor + */ + PluginDescriptor toPluginDescriptor(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java new file mode 100755 index 0000000..7d4b572 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptor.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.DependencyPlugin; + +import java.util.List; + +/** + * 插件信息 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginDescriptor { + + /** + * 获取插件id + * @return String + */ + String getPluginId(); + + /** + * 获取插件版本 + * @return String + */ + String getPluginVersion(); + + /** + * 获取插件引导类 + * @return String + */ + String getPluginBootstrapClass(); + + /** + * 获取插件路径 + * @return Path + */ + String getPluginPath(); + + /** + * 获取插件描述 + * @return String + */ + String getDescription(); + + /** + * 获取插件所能安装到主程序的版本 + * @return String + */ + String getRequires(); + + /** + * 获取插件提供开发者 + * @return String + */ + String getProvider(); + + /** + * 获取插件 license + * @return String + */ + String getLicense(); + + /** + * 获取当前插件依赖 + * @return List + */ + List getDependencyPlugin(); + + /** + * 得到插件类型 + * @return 插件类型 + */ + PluginType getType(); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java new file mode 100755 index 0000000..6e122b3 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginDescriptorLoader.java @@ -0,0 +1,43 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.core.exception.PluginException; + +import java.nio.file.Path; + +/** + * 插件描述加载者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginDescriptorLoader extends AutoCloseable{ + + + + /** + * 加载 PluginDescriptor + * @param location 引导配置文件路径 + * @return PluginDescriptor + * @throws PluginException 加载异常 + */ + InsidePluginDescriptor load(Path location) throws PluginException; + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java new file mode 100755 index 0000000..f3e9d7f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginLibInfo.java @@ -0,0 +1,47 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * 插件依赖包信息 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +@AllArgsConstructor +@Getter +@EqualsAndHashCode +@ToString +public class PluginLibInfo { + + /** + * 路径 + */ + private final String path; + + /** + * 是否加载到主程序中 + */ + private final boolean loadToMain; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java new file mode 100755 index 0000000..ca3401d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginResourcesConfig.java @@ -0,0 +1,107 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * 插件 ResourcesDefine 文件定义 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginResourcesConfig { + + private Set dependenciesIndex; + private Set loadMainResourceIncludes; + private Set loadMainResourceExcludes; + + public static PluginResourcesConfig parse(List fileLines){ + final PluginResourcesConfig pluginResourcesConfig = new PluginResourcesConfig(); + if(ObjectUtils.isEmpty(fileLines)){ + return pluginResourcesConfig; + } + + Set dependenciesIndex = new HashSet<>(); + Set loadMainResourceIncludes = new HashSet<>(); + Set loadMainResourceExcludes = new HashSet<>(); + + int i = 0; + + for (String fileLine : fileLines) { + if(ObjectUtils.isEmpty(fileLine)){ + continue; + } + if(Objects.equals(fileLine, PackageStructure.RESOURCES_DEFINE_DEPENDENCIES)){ + i = 1; + continue; + } else if(Objects.equals(fileLine, PackageStructure.RESOURCES_DEFINE_LOAD_MAIN_INCLUDES)){ + i = 2; + continue; + } else if(Objects.equals(fileLine, PackageStructure.RESOURCES_DEFINE_LOAD_MAIN_EXCLUDES)){ + i = 3; + continue; + } + if(i == 1){ + dependenciesIndex.add(fileLine); + } else if(i == 2){ + loadMainResourceIncludes.add(fileLine); + } else if(i == 3){ + loadMainResourceExcludes.add(fileLine); + } + } + pluginResourcesConfig.setDependenciesIndex(dependenciesIndex); + pluginResourcesConfig.setLoadMainResourceIncludes(loadMainResourceIncludes); + pluginResourcesConfig.setLoadMainResourceExcludes(loadMainResourceExcludes); + return pluginResourcesConfig; + } + + + public Set getDependenciesIndex() { + return dependenciesIndex; + } + + public void setDependenciesIndex(Set dependenciesIndex) { + this.dependenciesIndex = dependenciesIndex; + } + + public Set getLoadMainResourceIncludes() { + return loadMainResourceIncludes; + } + + public void setLoadMainResourceIncludes(Set loadMainResourceIncludes) { + this.loadMainResourceIncludes = loadMainResourceIncludes; + } + + public Set getLoadMainResourceExcludes() { + return loadMainResourceExcludes; + } + + public void setLoadMainResourceExcludes(Set loadMainResourceExcludes) { + this.loadMainResourceExcludes = loadMainResourceExcludes; + } + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java new file mode 100755 index 0000000..86e2bbb --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/PluginType.java @@ -0,0 +1,105 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.PackageType; +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.core.exception.PluginException; + +import java.util.Objects; + +/** + * 插件类型 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public enum PluginType { + + /** + * 开发模式目录 + */ + DEV(PackageType.PLUGIN_PACKAGE_TYPE_DEV), + + /** + * jar文件 + */ + JAR(PackageType.MAIN_PACKAGE_TYPE_JAR), + + /** + * jar-outer 文件 + */ + JAR_OUTER(PackageType.MAIN_PACKAGE_TYPE_JAR_OUTER), + + /** + * zip 文件 + */ + ZIP(PackageType.PLUGIN_PACKAGE_TYPE_ZIP), + + /** + * zip-outer 文件 + */ + ZIP_OUTER(PackageType.PLUGIN_PACKAGE_TYPE_ZIP_OUTER), + + /** + * 生产模式目录 + */ + DIR(PackageType.PLUGIN_PACKAGE_TYPE_DIR); + + + public final String name; + + PluginType(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static PluginType byName(String packageType){ + if(Objects.equals(packageType, PluginType.DEV.getName())){ + return PluginType.DEV; + } else if(Objects.equals(packageType, PluginType.JAR.getName())){ + return PluginType.JAR; + } else if(Objects.equals(packageType, PluginType.JAR_OUTER.getName())){ + return PluginType.JAR_OUTER; + } else if(Objects.equals(packageType, PluginType.ZIP.getName())){ + return PluginType.ZIP; + } else if(Objects.equals(packageType, PluginType.ZIP_OUTER.getName())){ + return PluginType.ZIP_OUTER; + } else if(Objects.equals(packageType, PluginType.DIR.getName())){ + return PluginType.DIR; + } else { + throw new PluginException("不能解析'" + packageType + "'打包类型的插件"); + } + } + + public static boolean isOuterPackage(PluginType pluginType){ + return pluginType == PluginType.JAR_OUTER || pluginType == PluginType.ZIP_OUTER; + } + + public static boolean isDirPackage(PluginType pluginType){ + return pluginType == PluginType.DIR; + } + + public static boolean isNestedPackage(PluginType pluginType){ + return pluginType == PluginType.JAR || pluginType == PluginType.ZIP; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java new file mode 100755 index 0000000..bd0f7d7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdDirPluginDescriptorLoader.java @@ -0,0 +1,131 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import com.gitee.starblues.utils.PropertiesUtils; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.FileUtils; +import org.springframework.context.ApplicationContext; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PackageStructure.*; + + +/** + * 生产环境目录式插件 PluginDescriptorLoader 加载者 + * 解析生产的dir + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.2 + */ +public class ProdDirPluginDescriptorLoader extends AbstractPluginDescriptorLoader{ + + public ProdDirPluginDescriptorLoader(PluginDescriptorDecrypt pluginDescriptorDecrypt) { + super(pluginDescriptorDecrypt); + } + + @Override + protected PluginMeta getPluginMetaInfo(Path location) throws Exception { + File file = new File(FilesUtils.joiningFilePath(location.toString(), resolvePath(PROD_MANIFEST_PATH))); + if(!file.exists()){ + return null; + } + Manifest manifest = new Manifest(); + String packageType = null; + String pluginMetaPath = null; + try (FileInputStream fileInputStream = new FileInputStream(file)){ + manifest.read(fileInputStream); + Attributes attributes = manifest.getMainAttributes(); + packageType = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_PACKAGE_TYPE); + pluginMetaPath = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_META_PATH); + } + if(packageType == null || pluginMetaPath == null){ + return null; + } + + File pluginMetaFile = new File(FilesUtils.joiningFilePath(location.toString(), pluginMetaPath)); + if(!pluginMetaFile.exists()){ + return null; + } + Properties properties = super.getDecryptProperties(new FileInputStream(pluginMetaFile)); + if(properties.isEmpty()){ + return null; + } + return new PluginMeta(packageType, properties); + } + + @Override + protected DefaultInsidePluginDescriptor create(PluginMeta pluginMeta, Path path) throws Exception { + DefaultInsidePluginDescriptor descriptor = super.create(pluginMeta, path); + String pathStr = path.toFile().getPath(); + descriptor.setPluginClassPath(FilesUtils.joiningFilePath( + pathStr, descriptor.getPluginClassPath() + )); + return descriptor; + } + + @Override + protected PluginResourcesConfig getPluginResourcesConfig(Path path, Properties properties) throws Exception { + String pathStr = path.toFile().getPath(); + String libIndexFile = getExistResourcesConfFile( + pathStr, PropertiesUtils.getValue(properties, PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG) + ); + + if(libIndexFile == null){ + return new PluginResourcesConfig(); + } + File libFile = new File(libIndexFile); + List lines = FileUtils.readLines(libFile, CHARSET_NAME); + return PluginResourcesConfig.parse(lines); + } + + protected String getExistResourcesConfFile(String rootPath, String libIndexPath){ + libIndexPath = resolvePath(libIndexPath); + if(ObjectUtils.isEmpty(libIndexPath)){ + // 如果配置为空, 直接从默认路径读取 + libIndexPath = FilesUtils.joiningFilePath(rootPath, resolvePath(PROD_RESOURCES_DEFINE_PATH)); + } else { + if(Files.exists(Paths.get(libIndexPath))){ + return libIndexPath; + } + libIndexPath = FilesUtils.joiningFilePath(rootPath, libIndexPath); + } + if(Files.exists(Paths.get(libIndexPath))){ + return libIndexPath; + } + return null; + } + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java new file mode 100755 index 0000000..103e482 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPackagePluginDescriptorLoader.java @@ -0,0 +1,136 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.PropertiesUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.apache.commons.io.IOUtils; +import org.springframework.context.ApplicationContext; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.Properties; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import static com.gitee.starblues.common.PluginDescriptorKey.PLUGIN_RESOURCES_CONFIG; + + +/** + * 生产环境打包好的插件 PluginDescriptorLoader 加载者 + * 解析 jar、zip + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class ProdPackagePluginDescriptorLoader extends AbstractPluginDescriptorLoader{ + + private PluginResourcesConfig pluginResourcesConfig; + + public ProdPackagePluginDescriptorLoader(PluginDescriptorDecrypt pluginDescriptorDecrypt) { + super(pluginDescriptorDecrypt); + } + + @Override + protected PluginMeta getPluginMetaInfo(Path location) throws Exception { + try (JarFile jarFile = new JarFile(location.toFile())){ + Manifest manifest = jarFile.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + String packageType = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_PACKAGE_TYPE); + String pluginMetaPath = ManifestKey.getValue(attributes, ManifestKey.PLUGIN_META_PATH); + if(packageType == null || pluginMetaPath == null){ + return null; + } + JarEntry jarEntry = jarFile.getJarEntry(pluginMetaPath); + if(jarEntry == null){ + return null; + } + Properties properties = super.getDecryptProperties(jarFile.getInputStream(jarEntry)); + if(properties.isEmpty()){ + return null; + } + pluginResourcesConfig = getPluginResourcesConfig(jarFile, properties); + return new PluginMeta(packageType, properties); + } + } + + @Override + protected PluginResourcesConfig getPluginResourcesConfig(Path path, Properties properties) throws Exception { + return pluginResourcesConfig; + } + + @Override + protected String getLibDir(DefaultInsidePluginDescriptor descriptor, String configPluginLibDir) { + if(PluginType.isNestedPackage(descriptor.getType())){ + return descriptor.getPluginLibDir(); + } else if(PluginType.isOuterPackage(descriptor.getType())){ + String pluginLibDir = descriptor.getPluginLibDir(); + if(ObjectUtils.isEmpty(pluginLibDir)){ + return super.getLibDir(descriptor, configPluginLibDir); + } + if(FilesUtils.isRelativePath(pluginLibDir)){ + return super.getLibDir(descriptor, configPluginLibDir); + } else { + return descriptor.getPluginLibDir(); + } + } else { + return super.getLibDir(descriptor, configPluginLibDir); + } + } + + @Override + protected String getLibPath(DefaultInsidePluginDescriptor descriptor, String configPluginLibDir, String index) { + if(PluginType.isNestedPackage(descriptor.getType())){ + String pluginLibDir = descriptor.getPluginLibDir(); + if(ObjectUtils.isEmpty(pluginLibDir)){ + return index; + } + if(index.startsWith(configPluginLibDir)){ + // 兼容解决旧版本中 jar/zip 包中, 依赖前缀携带 配置的 lib 路径 + return index; + } + return FilesUtils.joiningZipPath(pluginLibDir, index); + } else { + return super.getLibPath(descriptor, configPluginLibDir, index); + } + } + + protected PluginResourcesConfig getPluginResourcesConfig(JarFile jarFile, Properties properties) throws Exception { + String pluginResourcesConf = PropertiesUtils.getValue(properties, PLUGIN_RESOURCES_CONFIG); + if(ObjectUtils.isEmpty(pluginResourcesConf)){ + return new PluginResourcesConfig(); + } + JarEntry jarEntry = jarFile.getJarEntry(pluginResourcesConf); + if(jarEntry == null){ + return new PluginResourcesConfig(); + } + InputStream jarFileInputStream = jarFile.getInputStream(jarEntry); + List lines = IOUtils.readLines(jarFileInputStream, PackageStructure.CHARSET_NAME); + return PluginResourcesConfig.parse(lines); + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java new file mode 100755 index 0000000..b8c1bb1 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/ProdPluginDescriptorLoader.java @@ -0,0 +1,65 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor; + +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.ResourceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; + +/** + * 生产环境插件描述加载者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class ProdPluginDescriptorLoader implements PluginDescriptorLoader{ + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private PluginDescriptorLoader target; + + private final PluginDescriptorDecrypt pluginDescriptorDecrypt; + + public ProdPluginDescriptorLoader(PluginDescriptorDecrypt pluginDescriptorDecrypt) { + this.pluginDescriptorDecrypt = pluginDescriptorDecrypt; + } + + @Override + public InsidePluginDescriptor load(Path location) throws PluginException { + if(ResourceUtils.isJarFile(location)){ + target = new ProdPackagePluginDescriptorLoader(pluginDescriptorDecrypt); + } else if(ResourceUtils.isZipFile(location)){ + target = new ProdPackagePluginDescriptorLoader(pluginDescriptorDecrypt); + } else if(ResourceUtils.isDirFile(location)){ + target = new ProdDirPluginDescriptorLoader(pluginDescriptorDecrypt); + } else { + logger.warn("不能解析文件: {}", location); + return null; + } + return target.load(location); + } + + @Override + public void close() throws Exception { + target.close(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/DefaultPluginDescriptorDecrypt.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/DefaultPluginDescriptorDecrypt.java new file mode 100755 index 0000000..d2a4761 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/DefaultPluginDescriptorDecrypt.java @@ -0,0 +1,134 @@ +package com.gitee.starblues.core.descriptor.decrypt; + +import com.gitee.starblues.common.PluginDescriptorKey; +import com.gitee.starblues.common.cipher.AbstractPluginCipher; +import com.gitee.starblues.common.cipher.PluginCipher; +import com.gitee.starblues.core.exception.PluginDecryptException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.decrypt.DecryptConfiguration; +import com.gitee.starblues.integration.decrypt.DecryptPluginConfiguration; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.PropertiesUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.util.ClassUtils; + +import java.util.*; + +/** + * 默认的 PluginDescriptorDecrypt + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class DefaultPluginDescriptorDecrypt implements PluginDescriptorDecrypt{ + + private final ApplicationContext applicationContext; + + private final DecryptConfiguration decryptConfig; + private final Map pluginDecryptConfig; + + public DefaultPluginDescriptorDecrypt(ApplicationContext applicationContext, + IntegrationConfiguration configuration) { + this.applicationContext = applicationContext; + + this.decryptConfig = configuration.decrypt(); + List plugins = decryptConfig.getPlugins(); + if(ObjectUtils.isEmpty(plugins)){ + this.pluginDecryptConfig = Collections.emptyMap(); + } else { + this.pluginDecryptConfig = new HashMap<>(plugins.size()); + for (DecryptPluginConfiguration plugin : plugins) { + pluginDecryptConfig.put(plugin.getPluginId(), plugin); + } + } + } + + @Override + public Properties decrypt(String pluginId, Properties properties) { + PluginCipher pluginCipher = getPluginCipher(pluginId); + if(pluginCipher == null){ + return properties; + } + try { + String bootstrapClass = PropertiesUtils.getValue(properties, PluginDescriptorKey.PLUGIN_BOOTSTRAP_CLASS); + String decrypt = pluginCipher.decrypt(bootstrapClass); + properties.setProperty(PluginDescriptorKey.PLUGIN_BOOTSTRAP_CLASS, decrypt); + return properties; + } catch (Exception e) { + throw new PluginDecryptException("插件[" + pluginId + "]解密失败. " + e.getMessage()); + } + } + + protected PluginCipher getPluginCipher(String pluginId){ + if(decryptConfig == null){ + return null; + } + Boolean enable = decryptConfig.getEnable(); + if(enable == null || !enable){ + // 没有启用 + return null; + } + Map props = decryptConfig.getProps(); + if(props == null){ + props = new HashMap<>(); + decryptConfig.setProps(props); + } + String className = decryptConfig.getClassName(); + if(ObjectUtils.isEmpty(pluginDecryptConfig)){ + // 没有配置具体插件的解密配置 + return getPluginCipherBean(className, props); + } + DecryptPluginConfiguration decryptPluginConfiguration = pluginDecryptConfig.get(pluginId); + if(decryptPluginConfiguration == null){ + // 当前插件没有配置解密配置, 说明不启用解密 + return null; + } + Map pluginParam = decryptPluginConfiguration.getProps(); + if(!ObjectUtils.isEmpty(pluginParam)){ + props.putAll(pluginParam); + } + return getPluginCipherBean(className, props); + } + + + protected PluginCipher getPluginCipherBean(String className, Map params){ + ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader(); + try { + if(defaultClassLoader == null){ + defaultClassLoader = this.getClass().getClassLoader(); + } + Class aClass = defaultClassLoader.loadClass(className); + + String error = "解密实现者[" + className + "]没有继承 [" + AbstractPluginCipher.class.getName() + "]"; + + if(aClass.isAssignableFrom(AbstractPluginCipher.class)){ + throw new PluginDecryptException(error); + } + Object bean = getBean(aClass); + if(bean instanceof AbstractPluginCipher){ + AbstractPluginCipher pluginCipher = (AbstractPluginCipher) bean; + pluginCipher.initParams(params); + return pluginCipher; + } else { + throw new PluginDecryptException(error); + } + } catch (ClassNotFoundException e) { + throw new PluginDecryptException("没有发现解密实现者: " + className); + } + } + + protected Object getBean(Class aClass){ + try { + return applicationContext.getBean(aClass); + } catch (Exception e1){ + try { + return aClass.getConstructor().newInstance(); + } catch (Exception e2){ + throw new PluginDecryptException(e2); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/EmptyPluginDescriptorDecrypt.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/EmptyPluginDescriptorDecrypt.java new file mode 100755 index 0000000..8bf02b4 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/EmptyPluginDescriptorDecrypt.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor.decrypt; + +import java.util.Properties; + +/** + * 空的插件解密 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class EmptyPluginDescriptorDecrypt implements PluginDescriptorDecrypt{ + @Override + public Properties decrypt(String pluginId, Properties properties) { + return properties; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/PluginDescriptorDecrypt.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/PluginDescriptorDecrypt.java new file mode 100755 index 0000000..6b3490e --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/descriptor/decrypt/PluginDescriptorDecrypt.java @@ -0,0 +1,42 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.descriptor.decrypt; + +import com.gitee.starblues.core.exception.PluginDecryptException; + +import java.util.Properties; + +/** + * 插件描述文件解密器 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public interface PluginDescriptorDecrypt { + + /** + * 解密 properties + * + * @param pluginId 插件id + * @param properties properties + * @return 解密后的 Properties + * @throws PluginDecryptException 插件解密异常 + */ + Properties decrypt(String pluginId, Properties properties) throws PluginDecryptException; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDecryptException.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDecryptException.java new file mode 100755 index 0000000..ea133c3 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDecryptException.java @@ -0,0 +1,61 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; + +/** + * 插件解密异常 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class PluginDecryptException extends PluginException{ + + public PluginDecryptException() { + super(); + } + + public PluginDecryptException(String message) { + super(message); + } + + public PluginDecryptException(Throwable cause) { + super(cause); + } + + public PluginDecryptException(String message, Throwable cause) { + super(message, cause); + } + + public PluginDecryptException(PluginDescriptor pluginDescriptor, String opType, Throwable cause) { + super(pluginDescriptor, opType, cause); + } + + public PluginDecryptException(PluginDescriptor pluginDescriptor, String message) { + super(pluginDescriptor, message); + } + + public PluginDecryptException(String pluginId, String opType, Throwable cause) { + super(pluginId, opType, cause); + } + + public PluginDecryptException(String pluginId, String message) { + super(pluginId, message); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java new file mode 100755 index 0000000..0d998d9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginDisabledException.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; + +/** + * 插件被禁用异常 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginDisabledException extends PluginException { + + public PluginDisabledException(PluginDescriptor pluginDescriptor) { + this(pluginDescriptor, null); + } + + public PluginDisabledException(PluginDescriptor pluginDescriptor, String opType) { + super("插件[" + MsgUtils.getPluginUnique(pluginDescriptor) + "]已被禁用, 不能" + + (!ObjectUtils.isEmpty(opType) ? opType : "操作")); + } + + + /** + * 检查插件是否被禁用 + * @param pluginInsideInfo 插件信息 + * @param configuration 集成配置 + * @param opType 操作类型 + */ + public static void checkDisabled(PluginInfo pluginInsideInfo, IntegrationConfiguration configuration, String opType){ + if(pluginInsideInfo.getPluginState() == PluginState.DISABLED + || configuration.isDisabled(pluginInsideInfo.getPluginId()) + || !configuration.isEnable(pluginInsideInfo.getPluginId())){ + throw new PluginDisabledException(pluginInsideInfo.getPluginDescriptor(), opType); + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java new file mode 100755 index 0000000..617d5b9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginException.java @@ -0,0 +1,71 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.utils.MsgUtils; + +import java.util.function.Supplier; + + +/** + * 插件异常 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginException extends RuntimeException{ + + public PluginException() { + super(); + } + + public PluginException(String message) { + super(message); + } + + public PluginException(Throwable cause) { + super(cause); + } + + public PluginException(String message, Throwable cause) { + super(message, cause); + } + + public PluginException(PluginDescriptor pluginDescriptor, String opType, Throwable cause) { + this(MsgUtils.getPluginUnique(pluginDescriptor), opType, cause); + } + + public PluginException(PluginDescriptor pluginDescriptor, String message) { + this(MsgUtils.getPluginUnique(pluginDescriptor), message); + } + + public PluginException(String pluginId, String opType, Throwable cause) { + super("插件[" + pluginId + "]" + opType + "失败. " + MsgUtils.getThrowableMsg(cause), cause); + } + + public PluginException(String pluginId, String message) { + super("插件[" + pluginId + "]" + MsgUtils.getThrowableMsg(message)); + } + + public static PluginException getPluginException(Throwable throwable, Supplier getException){ + if(throwable instanceof PluginException){ + return (PluginException) throwable; + } + return getException.get(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java new file mode 100755 index 0000000..7718c13 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/exception/PluginProhibitStopException.java @@ -0,0 +1,33 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.exception; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.utils.MsgUtils; + +/** + * 插件禁止停止异常 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginProhibitStopException extends PluginException { + + public PluginProhibitStopException(PluginDescriptor pluginDescriptor, String message) { + super(pluginDescriptor, "被禁止卸载. " + MsgUtils.getThrowableMsg(message)); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java new file mode 100755 index 0000000..2ae933a --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/JavaMainResourcePatternDefiner.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher; + +import com.gitee.starblues.core.classloader.MainResourcePatternDefiner; + +import java.util.HashSet; +import java.util.Set; + +/** + * java 内部包匹配定义 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class JavaMainResourcePatternDefiner implements MainResourcePatternDefiner { + + protected final Set includes = new HashSet<>(); + + public JavaMainResourcePatternDefiner(){ + // java 内部的包匹配定义 + addJava(); + addJavax(); + addSun(); + addJdk(); + addJavaXml(); + } + + protected void addJava(){ + includes.add("java/**"); + } + + protected void addJavax(){ + includes.add("javax/**"); + includes.add("org/ietf/jgss/**"); + } + + protected void addJdk(){ + includes.add("jdk/**"); + // jdk.internal.vm.compiler + includes.add("org/graalvm/**"); + // jdk.internal.vm.compiler.management + includes.add("org/graalvm/compiler/hotspot/management/**"); + // jdk.hotspot.agent + includes.add("images/toolbarButtonGraphics/general/**"); + includes.add("toolbarButtonGraphics/**"); + } + + protected void addJavaXml(){ + // jdk.xml.dom + includes.add("org/w3c/dom/**"); + includes.add("org/xml/sax/**"); + includes.add("org/jcp/xml/dsig/internal**"); + } + + protected void addSun(){ + includes.add("com/sun/**"); + includes.add("sun/**"); + } + + @Override + public Set getIncludePatterns() { + return includes; + } + + @Override + public Set getExcludePatterns() { + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/AbstractPluginLauncher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/AbstractPluginLauncher.java new file mode 100755 index 0000000..1d7c251 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/AbstractPluginLauncher.java @@ -0,0 +1,62 @@ +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.loader.launcher.AbstractLauncher; +import com.gitee.starblues.spring.SpringPluginHook; + +/** + * 插件启动者 + * + * @author starBlues + * @version 3.1.1 + * @since 3.1.1 + */ +public abstract class AbstractPluginLauncher extends AbstractLauncher { + + protected final PluginInteractive pluginInteractive; + protected final PluginLaunchInvolved pluginLaunchInvolved; + + public AbstractPluginLauncher(PluginInteractive pluginInteractive, + PluginLaunchInvolved pluginLaunchInvolved) { + this.pluginInteractive = pluginInteractive; + this.pluginLaunchInvolved = pluginLaunchInvolved; + } + + @Override + protected final ClassLoader createClassLoader(String... args) throws Exception { + ClassLoader pluginClassLoader = createPluginClassLoader(args); + pluginInteractive.getPluginInsideInfo().setClassLoader(pluginClassLoader); + return pluginClassLoader; + } + + @Override + protected SpringPluginHook launch(ClassLoader classLoader, String... args) throws Exception { + PluginInsideInfo pluginInsideInfo = pluginInteractive.getPluginInsideInfo(); + pluginLaunchInvolved.before(pluginInsideInfo, classLoader); + try { + SpringPluginHook springPluginHook = (SpringPluginHook) new PluginMethodRunner(pluginInteractive) + .run(classLoader); + if(springPluginHook == null){ + throw new PluginException("插件返回的 SpringPluginHook 不能为空"); + } + pluginLaunchInvolved.after(pluginInsideInfo, classLoader, springPluginHook); + return new SpringPluginHookWrapper(springPluginHook, pluginInsideInfo, pluginLaunchInvolved, classLoader); + } catch (Throwable throwable){ + pluginLaunchInvolved.failure(pluginInsideInfo,classLoader, throwable); + throw throwable; + } + } + + /** + * 创建插件的classloader + * @param args 参数 + * @return ClassLoader + * @throws Exception 创建ClassLoader异常 + */ + protected abstract ClassLoader createPluginClassLoader(String... args) throws Exception; + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java new file mode 100755 index 0000000..37b1e93 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/BasicMainResourcePatternDefiner.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.classloader.MainResourcePatternDefiner; +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * 基本的主程序资源匹配定义 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class BasicMainResourcePatternDefiner implements MainResourcePatternDefiner { + + private final String mainPackageName; + + public BasicMainResourcePatternDefiner(String mainPackageName) { + this.mainPackageName = ObjectUtils.changePackageToMatch(mainPackageName); + } + + @Override + public Set getIncludePatterns() { + Set includePatterns = new HashSet<>(); + includePatterns.add(mainPackageName); + return includePatterns; + } + + @Override + public Set getExcludePatterns() { + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java new file mode 100755 index 0000000..e925ffe --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/CacheRegistryInfo.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * 可缓存的注册信息 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class CacheRegistryInfo implements RegistryInfo { + + private final Map registryInfo = new ConcurrentHashMap<>(); + + @Override + public void addRegistryInfo(String key, Object value) { + registryInfo.put(key, value); + } + + @SuppressWarnings("unchecked") + @Override + public T getRegistryInfo(String key) { + Object o = registryInfo.get(key); + if(o == null){ + return null; + } + return (T) o; + } + + @Override + public T getRegistryInfo(String key, Supplier notExistCreate) { + T t = getRegistryInfo(key); + if(t != null){ + return t; + } + t = notExistCreate.get(); + registryInfo.put(key, t); + return t; + } + + @Override + public void removeRegistryInfo(String key) { + registryInfo.remove(key); + } + + @Override + public void clearRegistryInfo() { + registryInfo.clear(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultMainResourcePatternDefiner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultMainResourcePatternDefiner.java new file mode 100755 index 0000000..2525e8e --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultMainResourcePatternDefiner.java @@ -0,0 +1,170 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.classloader.MainResourcePatternDefiner; +import com.gitee.starblues.core.launcher.JavaMainResourcePatternDefiner; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.ApplicationContext; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 主程序资源匹配定义 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class DefaultMainResourcePatternDefiner extends JavaMainResourcePatternDefiner { + + private static final String FRAMEWORK = "com/gitee/starblues/**"; + + public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; + + private final String mainPackage; + private final IntegrationConfiguration configuration; + private final ApplicationContext applicationContext; + + public DefaultMainResourcePatternDefiner(IntegrationConfiguration configuration, ApplicationContext applicationContext) { + this.mainPackage = configuration.mainPackage(); + this.configuration = configuration; + this.applicationContext = applicationContext; + } + + @Override + public Set getIncludePatterns() { + Set includeResourcePatterns = super.getIncludePatterns(); + // add mainPackage + includeResourcePatterns.add(ObjectUtils.changePackageToMatch(mainPackage)); + // add framework + includeResourcePatterns.add(FRAMEWORK); + addWebIncludeResourcePatterns(includeResourcePatterns); + addApiDoc(includeResourcePatterns); + addDbDriver(includeResourcePatterns); + addMainDependencyFramework(includeResourcePatterns); + + addIdea(includeResourcePatterns); + addLog(includeResourcePatterns); + + // add extension + List extensionPatternDefiners = getExtensionPatternDefiners(); + for (MainResourcePatternDefiner extensionPatternDefiner : extensionPatternDefiners) { + Set includePatterns = extensionPatternDefiner.getIncludePatterns(); + if(!ObjectUtils.isEmpty(includePatterns)){ + includeResourcePatterns.addAll(includePatterns); + } + } + + return includeResourcePatterns; + } + + @Override + public Set getExcludePatterns() { + Set excludeResourcePatterns = new HashSet<>(); + excludeResourcePatterns.add(FACTORIES_RESOURCE_LOCATION); + + // add extension + List extensionPatternDefiners = getExtensionPatternDefiners(); + for (MainResourcePatternDefiner extensionPatternDefiner : extensionPatternDefiners) { + Set excludePatterns = extensionPatternDefiner.getExcludePatterns(); + if(!ObjectUtils.isEmpty(excludePatterns)){ + excludeResourcePatterns.addAll(excludePatterns); + } + } + return excludeResourcePatterns; + } + + protected void addWebIncludeResourcePatterns(Set patterns) { + patterns.add("org/springframework/web/**"); + patterns.add("org/springframework/http/**"); + patterns.add("org/springframework/remoting/**"); + patterns.add("org/springframework/ui/**"); + + patterns.add("org/springframework/boot/autoconfigure/http/**"); + patterns.add("org/springframework/boot/autoconfigure/web/**"); + patterns.add("org/springframework/boot/autoconfigure/websocket/**"); + patterns.add("org/springframework/boot/autoconfigure/webservices/**"); + patterns.add("org/springframework/boot/autoconfigure/jackson/**"); + + patterns.add("com/fasterxml/jackson/**"); + } + + protected void addApiDoc(Set patterns) { + patterns.add("springfox/documentation/**"); + patterns.add("io/swagger/**"); + patterns.add("org/springdoc/**"); + } + + protected void addDbDriver(Set patterns) { + // mysql + patterns.add("com/mysql/**"); + // oracle + patterns.add("oracle/jdbc/**"); + // sqlserver + patterns.add("com/microsoft/jdbc/sqlserver/**"); + // DB2 + patterns.add("com/ibm/db2/jdbc/**"); + // DB2/AS400 + patterns.add("com/ibm/as400/**"); + // Informix + patterns.add("com/informix/jdbc/**"); + // Hypersonic + patterns.add("org/hsql/**"); + // MS SQL + patterns.add("com/microsoft/jdbc/**"); + // Postgres + patterns.add("org/postgresql/**"); + // Sybase + patterns.add("com/sybase/jdbc2/**"); + // Weblogic + patterns.add("weblogic/jdbc/**"); + // h2 + patterns.add("jdbc/h2/**"); + } + + protected void addMainDependencyFramework(Set patterns) { + patterns.add("com/github/benmanes/caffeine/cache/**"); + } + + protected void addIdea(Set patterns) { + // idea debug agent + patterns.add("com/intellij/rt/debugger/agent/**"); + } + + protected void addLog(Set patterns) { + if(Boolean.FALSE.equals(configuration.pluginFollowLog())){ + return; + } + patterns.add("org/slf4j/**"); + } + + + /** + * 获取扩展的 MainResourcePatternDefiner + * + * @return List + */ + private List getExtensionPatternDefiners() { + return SpringBeanUtils.getBeans(applicationContext, MainResourcePatternDefiner.class); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java new file mode 100755 index 0000000..ef39601 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/DefaultPluginInteractive.java @@ -0,0 +1,87 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.extract.DefaultExtractFactory; +import com.gitee.starblues.spring.extract.ExtractFactory; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; + +/** + * 默认的插件交互实现 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DefaultPluginInteractive implements PluginInteractive{ + + private final PluginInsideInfo pluginInsideInfo; + private final MainApplicationContext mainApplicationContext; + private final IntegrationConfiguration configuration; + private final InvokeSupperCache invokeSupperCache; + private final OpExtractFactory opExtractFactory; + + public DefaultPluginInteractive(PluginInsideInfo pluginInsideInfo, + MainApplicationContext mainApplicationContext, + IntegrationConfiguration configuration, + InvokeSupperCache invokeSupperCache) { + this.pluginInsideInfo = pluginInsideInfo; + this.mainApplicationContext = mainApplicationContext; + this.configuration = configuration; + this.invokeSupperCache = invokeSupperCache; + this.opExtractFactory = createOpExtractFactory(); + } + + protected OpExtractFactory createOpExtractFactory(){ + DefaultExtractFactory defaultExtractFactory = (DefaultExtractFactory)ExtractFactory.getInstant(); + return (OpExtractFactory) defaultExtractFactory.getTarget(); + } + + @Override + public InsidePluginDescriptor getPluginDescriptor() { + return pluginInsideInfo.getPluginDescriptor(); + } + + @Override + public PluginInsideInfo getPluginInsideInfo() { + return pluginInsideInfo; + } + + @Override + public MainApplicationContext getMainApplicationContext() { + return mainApplicationContext; + } + + @Override + public IntegrationConfiguration getConfiguration() { + return configuration; + } + + @Override + public InvokeSupperCache getInvokeSupperCache() { + return invokeSupperCache; + } + + @Override + public OpExtractFactory getOpExtractFactory() { + return opExtractFactory; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginCoexistLauncher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginCoexistLauncher.java new file mode 100755 index 0000000..dbcc89d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginCoexistLauncher.java @@ -0,0 +1,43 @@ +package com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.classloader.PluginGeneralUrlClassLoader; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.loader.classloader.GeneralUrlClassLoader; +import com.gitee.starblues.loader.launcher.LauncherContext; +import lombok.extern.slf4j.Slf4j; + +/** + * 插件共享式启动引导 + * + * @author starBlues + * @since 3.0.4 + * @version 3.1.0 + */ +@Slf4j +public class PluginCoexistLauncher extends AbstractPluginLauncher { + + + public PluginCoexistLauncher(PluginInteractive pluginInteractive, + PluginLaunchInvolved pluginLaunchInvolved) { + super(pluginInteractive, pluginLaunchInvolved); + } + + @Override + protected ClassLoader createPluginClassLoader(String... args) throws Exception { + PluginGeneralUrlClassLoader classLoader = new PluginGeneralUrlClassLoader( + pluginInteractive.getPluginDescriptor().getPluginId(), + getParentClassLoader()); + classLoader.addResource(pluginInteractive.getPluginDescriptor()); + return classLoader; + } + + protected GeneralUrlClassLoader getParentClassLoader() throws Exception { + ClassLoader contextClassLoader = LauncherContext.getMainClassLoader(); + if(contextClassLoader instanceof GeneralUrlClassLoader){ + return (GeneralUrlClassLoader) contextClassLoader; + } else { + throw new Exception("非法父类加载器: " + contextClassLoader.getClass().getName()); + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java new file mode 100755 index 0000000..f9c2f6e --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginInteractive.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.spring.invoke.InvokeSupperCache; + +/** + * 插件交互接口 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginInteractive { + + /** + * 获取插件信息 + * @return PluginDescriptor + */ + InsidePluginDescriptor getPluginDescriptor(); + + /** + * 获取插件内部信息 + * @return PluginInsideInfo + */ + PluginInsideInfo getPluginInsideInfo(); + + /** + * 获取主程序的 MainApplicationContext + * @return MainApplicationContext + */ + MainApplicationContext getMainApplicationContext(); + + /** + * 获取主程序对框架集成配置信息 + * @return IntegrationConfiguration + */ + IntegrationConfiguration getConfiguration(); + + /** + * 获取远程调用缓存 + * @return InvokeSupperCache + */ + InvokeSupperCache getInvokeSupperCache(); + + /** + * 获取业务扩展功能的工厂 + * @return OpExtractFactory + */ + OpExtractFactory getOpExtractFactory(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginIsolationLauncher.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginIsolationLauncher.java new file mode 100755 index 0000000..dffc257 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginIsolationLauncher.java @@ -0,0 +1,114 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.classloader.*; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import com.gitee.starblues.loader.classloader.resource.loader.DefaultResourceLoaderFactory; +import com.gitee.starblues.loader.classloader.resource.loader.ResourceLoaderFactory; +import com.gitee.starblues.loader.launcher.LauncherContext; +import com.gitee.starblues.loader.utils.ResourceUtils; +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * 插件隔离式启动引导 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class PluginIsolationLauncher extends AbstractPluginLauncher { + + private static final Map CLASS_LOADER_CACHE = new WeakHashMap<>(); + + protected final PluginInsideInfo pluginInsideInfo; + protected final MainResourceMatcher mainResourceMatcher; + + public PluginIsolationLauncher(PluginInteractive pluginInteractive, + PluginLaunchInvolved pluginLaunchInvolved) { + super(pluginInteractive, pluginLaunchInvolved); + this.pluginInsideInfo = pluginInteractive.getPluginInsideInfo(); + this.mainResourceMatcher = getMainResourceMatcher(pluginInteractive); + } + + protected MainResourceMatcher getMainResourceMatcher(PluginInteractive pluginInteractive){ + MainApplicationContext mainApplicationContext = pluginInteractive.getMainApplicationContext(); + // 获取主程序定义的资源匹配 + List mainResourceMatchers = + SpringBeanCustomUtils.getBeans(mainApplicationContext, MainResourceMatcher.class); + + List resourceMatchers = new ArrayList<>(mainResourceMatchers); + // 新增插件定义的资源匹配 + resourceMatchers.add(new DefaultMainResourceMatcher( + new PluginMainResourcePatternDefiner(pluginInteractive) + )); + return new ComposeMainResourceMatcher(resourceMatchers); + } + + @Override + protected ClassLoader createPluginClassLoader(String... args) throws Exception { + PluginClassLoader pluginClassLoader = getPluginClassLoader(); + pluginClassLoader.addResource(pluginInsideInfo.getPluginDescriptor()); + return pluginClassLoader; + } + + @Override + protected SpringPluginHook launch(ClassLoader classLoader, String... args) throws Exception { + SpringPluginHook springPluginHook = super.launch(classLoader, args); + ResourceUtils.release(classLoader); + return springPluginHook; + } + + protected synchronized PluginClassLoader getPluginClassLoader() throws Exception { + String pluginId = pluginInsideInfo.getPluginId(); + String key = MsgUtils.getPluginUnique(pluginInsideInfo.getPluginDescriptor()); + PluginClassLoader classLoader = CLASS_LOADER_CACHE.get(key); + if(classLoader != null){ + return classLoader; + } + PluginClassLoader pluginClassLoader = new PluginClassLoader( + pluginId, getParentClassLoader(), getParentClassLoader(), getResourceLoaderFactory(), + mainResourceMatcher + ); + CLASS_LOADER_CACHE.put(key, pluginClassLoader); + return pluginClassLoader; + } + + protected ResourceLoaderFactory getResourceLoaderFactory(){ + return new DefaultResourceLoaderFactory(pluginInsideInfo.getPluginId()); + } + + protected GenericClassLoader getParentClassLoader() throws Exception { + ClassLoader contextClassLoader = LauncherContext.getMainClassLoader(); + if(contextClassLoader instanceof GenericClassLoader){ + return (GenericClassLoader) contextClassLoader; + } else { + throw new Exception("非法父类加载器: " + contextClassLoader.getClass().getName()); + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java new file mode 100755 index 0000000..d469657 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMainResourcePatternDefiner.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.classloader.MainResourcePatternDefiner; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * 定义插件从主程序加载资源的匹配 + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class PluginMainResourcePatternDefiner implements MainResourcePatternDefiner { + + private final InsidePluginDescriptor descriptor; + + public PluginMainResourcePatternDefiner(PluginInteractive pluginInteractive) { + this.descriptor = pluginInteractive.getPluginDescriptor(); + } + + @Override + public Set getIncludePatterns() { + Set includeResourcePatterns = new HashSet<>(); + // 配置插件自定义从主程序加载的资源匹配 + Set includeMainResourcePatterns = descriptor.getIncludeMainResourcePatterns(); + if(!ObjectUtils.isEmpty(includeMainResourcePatterns)){ + includeResourcePatterns.addAll(includeMainResourcePatterns); + } + return includeResourcePatterns; + } + + @Override + public Set getExcludePatterns() { + Set excludeResourcePatterns = new HashSet<>(); + Set excludeMainResourcePatterns = descriptor.getExcludeMainResourcePatterns(); + if(!ObjectUtils.isEmpty(excludeMainResourcePatterns)){ + excludeResourcePatterns.addAll(excludeMainResourcePatterns); + } + return excludeResourcePatterns; + } + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java new file mode 100755 index 0000000..5935186 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/PluginMethodRunner.java @@ -0,0 +1,96 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.loader.launcher.runner.MethodRunner; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ReflectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; + +/** + * 插件方法运行器。 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginMethodRunner extends MethodRunner { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private static final String PLUGIN_RUN_METHOD_NAME = "run"; + + private final PluginInteractive pluginInteractive; + + public PluginMethodRunner(PluginInteractive pluginInteractive) { + super(pluginInteractive.getPluginDescriptor().getPluginBootstrapClass(), PLUGIN_RUN_METHOD_NAME, new String[]{}); + this.pluginInteractive = pluginInteractive; + String args = pluginInteractive.getPluginDescriptor().getArgs(); + if(!ObjectUtils.isEmpty(args)){ + super.args = args.split(" "); + } + } + + @Override + protected Class loadRunClass(ClassLoader classLoader) throws Exception { + try { + return super.loadRunClass(classLoader); + } catch (Throwable e){ + InsidePluginDescriptor pluginDescriptor = pluginInteractive.getPluginDescriptor(); + String pluginUnique = MsgUtils.getPluginUnique(pluginDescriptor); + if(e instanceof ClassNotFoundException){ + String error = "插件[" + pluginUnique + "]没有发现" + "[" + className + "]引导类"; + if(pluginInteractive.getConfiguration().environment() == RuntimeMode.DEV){ + error = error + ", 请确保已经编译!"; + } + throw new ClassNotFoundException(error); + } else if(e instanceof NoClassDefFoundError){ + String error = "插件[" + pluginUnique + "]没有发现依赖类: [" + e.getMessage() + "], " + + "请确保插件依赖被完整加载!"; + throw new NoClassDefFoundError(error); + } + throw e; + } + } + + @Override + protected Object runMethod(Class runClass) throws Exception { + Method runMethod = ReflectionUtils.findMethod(runClass, runMethodName, Class.class, String[].class); + if(runMethod == null) { + throw new NoSuchMethodException(runClass.getName() + "." + runMethodName + + "(Class arg0, String[] arg1)"); + } + Object instance = getInstance(runClass); + setPluginInteractive(instance); + runMethod.setAccessible(true); + return runMethod.invoke(instance, runClass, this.args); + } + + private void setPluginInteractive(Object launchObject) throws Exception { + if(launchObject == null){ + return; + } + ReflectionUtils.setAttribute(launchObject, "setPluginInteractive", pluginInteractive); + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java new file mode 100755 index 0000000..ddea632 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/RegistryInfo.java @@ -0,0 +1,65 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; +import java.util.function.Supplier; + +/** + * 注册的信息接口 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface RegistryInfo { + + + /** + * 添加注册的信息 + * @param key 注册信息key + * @param value 注册信息值 + */ + void addRegistryInfo(String key, Object value); + + /** + * 得到注册信息 + * @param key 注册信息key + * @param 返回类型泛型 + * @return 注册信息的值 + */ + T getRegistryInfo(String key); + + /** + * 得到注册信息 + * @param key 注册信息key + * @param notExistCreate 不存在的话, 进行创建操作 + * @param 返回类型泛型 + * @return 注册信息的值 + */ + T getRegistryInfo(String key, Supplier notExistCreate); + + + /** + * 移除注册信息 + * @param key 注册信息key + */ + void removeRegistryInfo(String key); + + /** + * 清除全部的注册信息 + */ + void clearRegistryInfo(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java new file mode 100755 index 0000000..31dbc99 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/SpringPluginHookWrapper.java @@ -0,0 +1,87 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin; + +import com.gitee.starblues.core.PluginCloseType; +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.exception.PluginProhibitStopException; +import com.gitee.starblues.core.launcher.plugin.involved.PluginLaunchInvolved; +import com.gitee.starblues.loader.PluginResourceStorage; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.WebConfig; +import com.gitee.starblues.utils.ResourceUtils; +import lombok.extern.slf4j.Slf4j; + +/** + * SpringPluginHook-Wrapper + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +@Slf4j +public class SpringPluginHookWrapper implements SpringPluginHook { + + private final SpringPluginHook target; + private final PluginInsideInfo pluginInsideInfo; + private final PluginLaunchInvolved pluginLaunchInvolved; + private final ClassLoader classLoader; + + public SpringPluginHookWrapper(SpringPluginHook target, PluginInsideInfo pluginInsideInfo, + PluginLaunchInvolved pluginLaunchInvolved, + ClassLoader classLoader) { + this.target = target; + this.pluginInsideInfo = pluginInsideInfo; + this.pluginLaunchInvolved = pluginLaunchInvolved; + this.classLoader = classLoader; + } + + @Override + public void stopVerify() throws PluginProhibitStopException { + target.stopVerify(); + } + + @Override + public ApplicationContext getApplicationContext() { + return target.getApplicationContext(); + } + + @Override + public WebConfig getWebConfig() { + return target.getWebConfig(); + } + + @Override + public void close(PluginCloseType closeType) throws Exception { + // 1. 关闭 application 等信息 + try { + target.close(closeType); + } catch (Exception e){ + log.error("关闭插件异常: {}", e.getMessage(), e); + } + // 2. 关闭 pluginLaunchInvolved + try { + pluginLaunchInvolved.close(pluginInsideInfo, classLoader); + } catch (Exception e){ + log.error("关闭插件异常: {}", e.getMessage(), e); + } + // 3. 关闭classloader + ResourceUtils.closeQuietly(classLoader); + // 4. 移除插件jar等信息 + PluginResourceStorage.removePlugin(pluginInsideInfo.getPluginId()); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java new file mode 100755 index 0000000..9b97d6b --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/DefaultPluginLaunchInvolved.java @@ -0,0 +1,95 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.PluginExtensionInfo; +import com.gitee.starblues.core.descriptor.PluginLibInfo; +import com.gitee.starblues.loader.PluginResourceStorage; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.spring.web.PluginStaticResourceResolver; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 默认的插件启动介入者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +@Slf4j +public class DefaultPluginLaunchInvolved implements PluginLaunchInvolved{ + + @Override + public void before(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception { + InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor(); + Set pluginLibInfo = descriptor.getPluginLibInfo(); + List libPath = null; + if(pluginLibInfo != null){ + libPath = pluginLibInfo.stream().map(PluginLibInfo::getPath).collect(Collectors.toList()); + } + PluginResourceStorage.addPlugin(descriptor.getPluginId(), descriptor.getPluginFileName(), libPath); + } + + @Override + public void after(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor(); + PluginStaticResourceResolver.parse(descriptor, classLoader, pluginHook.getWebConfig()); + setExtensionInfoSupplier(pluginInsideInfo, pluginHook); + } + + @Override + public void close(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception { + InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor(); + String pluginId = descriptor.getPluginId(); + PluginStaticResourceResolver.remove(pluginId); + } + + private void setExtensionInfoSupplier(PluginInsideInfo pluginInsideInfo, SpringPluginHook pluginHook){ + pluginInsideInfo.setExtensionInfoSupplier(()->{ + // 设置插件自主扩展信息 + ApplicationContext applicationContext = pluginHook.getApplicationContext(); + List beans = SpringBeanCustomUtils.getBeans(applicationContext, + PluginExtensionInfo.class); + if(ObjectUtils.isEmpty(beans)){ + return new HashMap<>(0); + } + Map extensionInfos = new HashMap<>(); + for (PluginExtensionInfo extensionInfoBean : beans) { + try { + Map extensionInfo = extensionInfoBean.extensionInfo(); + if(!ObjectUtils.isEmpty(extensionInfo)){ + extensionInfos.putAll(extensionInfo); + } + } catch (Exception e){ + log.error(e.getMessage(), e); + } + } + return extensionInfos; + }); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java new file mode 100755 index 0000000..a1296c8 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginApplicationContextGetter.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringPluginHook; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 插件 applicationContext 获取者 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class PluginApplicationContextGetter implements PluginLaunchInvolved{ + + private static final Map PLUGIN_CONTEXTS = new ConcurrentHashMap<>(); + + @Override + public void after(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor(); + PLUGIN_CONTEXTS.put(descriptor.getPluginId(), pluginHook.getApplicationContext()); + } + + @Override + public void close(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception { + InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor(); + PLUGIN_CONTEXTS.remove(descriptor.getPluginId()); + } + + public static ApplicationContext get(String pluginId){ + return PLUGIN_CONTEXTS.get(pluginId); + } + + public static Map get(){ + return Collections.unmodifiableMap(PLUGIN_CONTEXTS); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java new file mode 100755 index 0000000..5ff3d5e --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolved.java @@ -0,0 +1,86 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.utils.OrderPriority; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 插件启动前后介入 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public interface PluginLaunchInvolved { + + /** + * 初始化。仅调用一次 + * @param applicationContext 主程序GenericApplicationContext + * @param configuration 集成配置 + */ + default void initialize(GenericApplicationContext applicationContext, IntegrationConfiguration configuration){} + + /** + * 启动之前 + * @param pluginInsideInfo 插件信息 + * @param classLoader 插件classloader + * @throws Exception 执行异常 + */ + default void before(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception{} + + /** + * 启动之后 + * @param pluginInsideInfo 插件信息 + * @param classLoader 插件classloader + * @param pluginHook 启动成功后插件返回的钩子 + * @throws Exception 执行异常 + */ + default void after(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, + SpringPluginHook pluginHook) throws Exception{} + + /** + * 启动失败 + * @param pluginInsideInfo 插件信息 + * @param classLoader 插件classloader + * @param throwable 异常信息 + * @throws Exception 执行异常 + */ + default void failure(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, Throwable throwable) throws Exception{} + + /** + * 关闭的时候 + * @param pluginInsideInfo 插件信息 + * @param classLoader 插件classloader + * @throws Exception 执行异常 + */ + default void close(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception{} + + /** + * 执行顺序 + * @return OrderPriority + */ + default OrderPriority order(){ + return OrderPriority.getMiddlePriority(); + } + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java new file mode 100755 index 0000000..dbbcd94 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/launcher/plugin/involved/PluginLaunchInvolvedFactory.java @@ -0,0 +1,116 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.launcher.plugin.involved; + +import com.gitee.starblues.core.PluginInsideInfo; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.spring.SpringPluginHook; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.ObjectUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 插件启动介入工厂 + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class PluginLaunchInvolvedFactory implements PluginLaunchInvolved{ + + private static final Logger logger = LoggerFactory.getLogger(PluginLaunchInvolvedFactory.class); + + private List pluginLaunchInvolvedList; + + + @Override + public void initialize(GenericApplicationContext applicationContext, IntegrationConfiguration configuration) { + this.pluginLaunchInvolvedList = getPluginLaunchInvolvedList(applicationContext); + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + try { + pluginLaunchInvolved.initialize(applicationContext, configuration); + } catch (Exception e){ + logger.error("[{}] execute initialize exception : {}", + pluginLaunchInvolved.getClass().getName(), e.getMessage(), e); + } + } + } + + protected List getPluginLaunchInvolvedList(GenericApplicationContext applicationContext){ + List pluginLaunchInvolvedList = getDefaultPluginLaunchInvolved(); + if(pluginLaunchInvolvedList == null){ + pluginLaunchInvolvedList = new ArrayList<>(); + } + Map pluginLaunchInvolvedMap = applicationContext.getBeansOfType(PluginLaunchInvolved.class); + if(!ObjectUtils.isEmpty(pluginLaunchInvolvedMap)){ + pluginLaunchInvolvedList.addAll(pluginLaunchInvolvedMap.values()); + } + pluginLaunchInvolvedList.sort(OrderUtils.orderPriority(PluginLaunchInvolved::order)); + return pluginLaunchInvolvedList; + } + + protected List getDefaultPluginLaunchInvolved(){ + List defaultPluginLaunchInvolved = new ArrayList<>(); + defaultPluginLaunchInvolved.add(new DefaultPluginLaunchInvolved()); + defaultPluginLaunchInvolved.add(new PluginApplicationContextGetter()); + return defaultPluginLaunchInvolved; + } + + @Override + public void before(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + pluginLaunchInvolved.before(pluginInsideInfo, classLoader); + } + } + + @Override + public void after(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, SpringPluginHook pluginHook) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + pluginLaunchInvolved.after(pluginInsideInfo, classLoader, pluginHook); + } + } + + @Override + public void failure(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, Throwable throwable) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + try { + pluginLaunchInvolved.failure(pluginInsideInfo, classLoader, throwable); + } catch (Exception e){ + logger.error("[{}] execute failure exception : {}", + pluginLaunchInvolved.getClass().getName(), e.getMessage(), e); + } + } + } + + @Override + public void close(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception { + for (PluginLaunchInvolved pluginLaunchInvolved : pluginLaunchInvolvedList) { + try { + pluginLaunchInvolved.close(pluginInsideInfo, classLoader); + } catch (Exception e){ + logger.error("[{}] execute close exception : {}", + pluginLaunchInvolved.getClass().getName(), e.getMessage(), e); + } + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java new file mode 100755 index 0000000..47ffca1 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/BasePluginScanner.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.scanner; + +import com.gitee.starblues.utils.ObjectUtils; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 基本的插件扫描者 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class BasePluginScanner implements PluginScanner{ + + private PathResolve pathResolve; + + public void setPathResolve(PathResolve pathResolve) { + this.pathResolve = pathResolve; + } + + @Override + public List scan(List rootDir) { + if(ObjectUtils.isEmpty(rootDir)){ + return Collections.emptyList(); + } + List pluginPaths = new ArrayList<>(); + if(pathResolve == null){ + return pluginPaths; + } + for (String dir : rootDir) { + if(ObjectUtils.isEmpty(dir)){ + continue; + } + File file = new File(dir); + if(!file.exists()){ + continue; + } + resolve(file, pluginPaths); + } + return pluginPaths; + } + + protected void resolve(File currentFile, List pluginPaths){ + if(currentFile == null || !currentFile.exists()){ + return; + } + Path currentPath = currentFile.toPath(); + currentPath = pathResolve.resolve(currentPath); + if(currentPath != null){ + pluginPaths.add(currentPath); + } else { + File[] files = currentFile.listFiles(); + if(files == null || files.length == 0){ + return; + } + for (File file : files) { + resolve(file, pluginPaths); + } + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java new file mode 100755 index 0000000..71ef46c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ComposePathResolve.java @@ -0,0 +1,64 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.scanner; + +import com.gitee.starblues.utils.ObjectUtils; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 组合的PathResolve + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ComposePathResolve implements PathResolve{ + + private final List pathResolves; + + public ComposePathResolve(PathResolve ...pathResolves) { + this(Arrays.asList(pathResolves)); + } + + public ComposePathResolve(List pathResolves) { + if(ObjectUtils.isEmpty(pathResolves)){ + this.pathResolves = new ArrayList<>(); + } else { + this.pathResolves = pathResolves; + } + } + + public void addPathResolve(PathResolve pathResolve){ + if(pathResolve != null){ + pathResolves.add(pathResolve); + } + } + + @Override + public Path resolve(Path path) { + for (PathResolve pathResolve : pathResolves) { + Path resolvePath = pathResolve.resolve(path); + if(resolvePath != null){ + return resolvePath; + } + } + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java new file mode 100755 index 0000000..9d68489 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/DevPathResolve.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.scanner; + +import com.gitee.starblues.common.PackageStructure; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +/** + * 开发环境路径解决器 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class DevPathResolve implements PathResolve{ + + private final List devCompilePackageNames = new ArrayList<>(); + + public DevPathResolve() { + addCompilePackageName(); + } + + protected void addCompilePackageName(){ + // 添加插件信息查询目录 + devCompilePackageNames.add("target".concat(File.separator).concat(PackageStructure.META_INF_NAME)); + } + + @Override + public Path resolve(Path path) { + for (String devCompilePackageName : devCompilePackageNames) { + String compilePackagePathStr = path.toString() + File.separator + devCompilePackageName; + Path compilePackagePath = Paths.get(compilePackagePathStr); + if(Files.exists(compilePackagePath)){ + return compilePackagePath; + } + } + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java new file mode 100755 index 0000000..810484a --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PathResolve.java @@ -0,0 +1,37 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.scanner; + +import java.nio.file.Path; + +/** + * 从路径中发现合适的插件 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PathResolve { + + /** + * 过滤并返回正确的路径 + * @param path 待过滤路径 + * @return path 处理后的路径, 返回null 表示不可用 + */ + Path resolve(Path path); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java new file mode 100755 index 0000000..04ee682 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/PluginScanner.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.scanner; + +import java.nio.file.Path; +import java.util.List; + +/** + * 插件扫描者 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface PluginScanner { + + /** + * 从 rootDir 集合中扫描出插件路径 + * @param rootDir 根目录 + * @return 扫描出的目录 + */ + List scan(List rootDir); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java new file mode 100755 index 0000000..c1efa78 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/scanner/ProdPathResolve.java @@ -0,0 +1,79 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.scanner; + +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.utils.FilesUtils; +import com.gitee.starblues.utils.OrderUtils; +import com.gitee.starblues.utils.ObjectUtils; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 生产环境目录解决器 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class ProdPathResolve implements PathResolve{ + + private final List packageSuffix = new ArrayList<>(); + + public ProdPathResolve(){ + // jar包 + addPackageSuffix(".jar"); + // zip包 + addPackageSuffix(".zip"); + } + + protected void addPackageSuffix(String name){ + if(ObjectUtils.isEmpty(name)){ + return; + } + packageSuffix.add(name); + } + + @Override + public Path resolve(Path path) { + if(isDirPlugin(path)){ + return path; + } + String fileName = path.getFileName().toString().toLowerCase(); + for (String suffixName : packageSuffix) { + boolean exist = fileName.endsWith(suffixName.toLowerCase()); + if(exist){ + return path; + } + } + return null; + } + + protected boolean isDirPlugin(Path path){ + File file = path.toFile(); + if(file.isFile()){ + return false; + } + + file = new File(FilesUtils.joiningFilePath(path.toString(), PackageStructure.resolvePath( + PackageStructure.PROD_MANIFEST_PATH + ))); + return file.exists() && file.isFile(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java new file mode 100755 index 0000000..9c72473 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/SemverVersionInspector.java @@ -0,0 +1,58 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.version; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.utils.ObjectUtils; +import com.github.zafarkhaja.semver.UnexpectedCharacterException; +import com.github.zafarkhaja.semver.Version; + +/** + * Semver标准版本检查 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public class SemverVersionInspector implements VersionInspector{ + + @Override + public void check(String version) throws PluginException { + try { + Version.valueOf(version); + } catch (Throwable e){ + String message = e.toString(); + if(ObjectUtils.isEmpty(message)){ + message = ""; + } else { + message = ": " + message; + } + throw new PluginException("版本号[" + version + "]非法" + message); + } + } + + @Override + public int compareTo(String version1, String version2) { + check(version1); + check(version2); + + Version v1 = Version.valueOf(version1); + Version v2 = Version.valueOf(version2); + return v1.compareTo(v2); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java new file mode 100755 index 0000000..87d95c5 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/core/version/VersionInspector.java @@ -0,0 +1,46 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.core.version; + +import com.gitee.starblues.core.exception.PluginException; + +/** + * 版本检查 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.1 + */ +public interface VersionInspector { + + /** + * 检查插件版本号是否合法 + * @param version 版本号 + * @throws PluginException 版本号不合法则抛出异常 + */ + void check(String version) throws PluginException; + + /** + * 比较 v1 和 v2版本. + * @param v1 版本号码1 + * @param v2 版本号码2 + * @return 如果 v1大于等于v2, 则返回大于等于0的数字, 否则返回小于0的数字 + * @throws PluginException 版本号不合法则抛出异常 + */ + int compareTo(String v1, String v2) throws PluginException;; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java new file mode 100755 index 0000000..883887d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/AutoIntegrationConfiguration.java @@ -0,0 +1,269 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration; + +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.integration.decrypt.DecryptConfiguration; +import com.gitee.starblues.utils.ResourceUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.util.List; +import java.util.Set; + +/** + * 自动集成的配置 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +@EqualsAndHashCode(callSuper = true) +@Component +@ConfigurationProperties(prefix = "plugin") +@Data +public class AutoIntegrationConfiguration extends DefaultIntegrationConfiguration{ + + public static final String ENABLE_KEY = "plugin.enable"; + public static final String ENABLE_STARTER_KEY = "plugin.enableStarter"; + + /** + * 是否启用插件功能 + */ + @Value("${enable:true}") + private Boolean enable; + + /** + * 运行模式 + * 开发环境: development、dev + * 生产/部署 环境: deployment、prod + */ + @Value("${runMode:dev}") + private String runMode; + + /** + * 主程序包名 + */ + @Value("${mainPackage:}") + private String mainPackage; + + /** + * 插件的路径 + */ + private List pluginPath; + + /** + * 上传的插件所存储的临时目录 + */ + @Value("${uploadTempPath:}") + private String uploadTempPath; + + /** + * 在卸载插件后, 备份插件的目录 + */ + @Value("${backupPath:backupPlugin}") + private String backupPath; + + /** + * 插件rest接口前缀. 默认: /plugins + */ + @Value("${pluginRestPathPrefix:/plugins}") + private String pluginRestPathPrefix; + + /** + * 是否启用插件id作为rest接口前缀, 默认为启用. + * 如果为启用, 则地址为 /pluginRestPathPrefix/pluginId + * pluginRestPathPrefix: 为pluginRestPathPrefix的配置值 + * pluginId: 为插件id + */ + @Value("${enablePluginIdRestPathPrefix:true}") + private Boolean enablePluginIdRestPathPrefix; + + /** + * 启用的插件id + */ + private Set enablePluginIds; + + /** + * 禁用的插件id, 禁用后系统不会启动该插件 + * 如果禁用所有插件, 则Set集合中返回一个字符: * + */ + private Set disablePluginIds; + + /** + * 设置初始化时插件启动的顺序 + */ + private List sortInitPluginIds; + + /** + * 当前主程序的版本号, 用于校验插件是否可安装. + * 插件中可通过插件配置信息 requires 来指定可安装的主程序版本 + * 如果为: 0.0.0 的话, 表示不校验 + */ + @Value("${version:0.0.0}") + private String version; + + /** + * 设置为true表示插件设置的requires的版本号完全匹配version版本号才可允许插件安装, 即: requires=x.y.z + * 设置为false表示插件设置的requires的版本号小于等于version值, 插件就可安装, 即requires<=x.y.z + * 默认为false + */ + @Value("${exactVersion:false}") + private Boolean exactVersion; + + /** + * 插是否扫描 swagger 接口 + */ + @Value("${pluginSwaggerScan:true}") + private Boolean pluginSwaggerScan; + + + /** + * 插件的配置文件 Profile 是否跟随主程序的 Profile 配置动态切换 + */ + @Value("${pluginFollowProfile:false}") + private Boolean pluginFollowProfile; + + /** + * 插件日志打印是否跟随主程序 + */ + @Value("${pluginFollowLog:false}") + private Boolean pluginFollowLog; + + /** + * 对插件启动时进行解密校验配置。默认为不启用 + */ + private DecryptConfiguration decrypt; + + @Override + public Boolean enable() { + if(enable == null){ + return true; + } + return enable; + } + + @Override + public RuntimeMode environment() { + return RuntimeMode.byName(runMode); + } + + @Override + public String mainPackage() { + return ResourceUtils.replacePackage(mainPackage); + } + + @Override + public List pluginPath() { + return pluginPath; + } + + @Override + public String uploadTempPath() { + if(ObjectUtils.isEmpty(uploadTempPath)){ + return super.uploadTempPath(); + } + return uploadTempPath; + } + + @Override + public String backupPath() { + if(ObjectUtils.isEmpty(backupPath)){ + return super.backupPath(); + } + return backupPath; + } + + @Override + public String pluginRestPathPrefix() { + if(pluginRestPathPrefix == null){ + return super.pluginRestPathPrefix(); + } else { + return pluginRestPathPrefix; + } + } + + @Override + public Boolean enablePluginIdRestPathPrefix() { + if(enablePluginIdRestPathPrefix == null){ + return super.enablePluginIdRestPathPrefix(); + } else { + return enablePluginIdRestPathPrefix; + } + } + + @Override + public Set enablePluginIds() { + return enablePluginIds; + } + + @Override + public Set disablePluginIds() { + return disablePluginIds; + } + + @Override + public List sortInitPluginIds() { + return sortInitPluginIds; + } + + @Override + public String version() { + return version; + } + + @Override + public Boolean exactVersion() { + return exactVersion; + } + + @Override + public Boolean pluginSwaggerScan() { + if(pluginSwaggerScan == null){ + return super.pluginSwaggerScan(); + } + return pluginSwaggerScan; + } + + @Override + public Boolean pluginFollowProfile() { + if(pluginFollowProfile == null){ + return super.pluginFollowProfile(); + } + return pluginFollowProfile; + } + + @Override + public Boolean pluginFollowLog() { + if(pluginFollowLog == null){ + return super.pluginFollowLog(); + } + return pluginFollowLog; + } + + @Override + public DecryptConfiguration decrypt() { + if(decrypt == null){ + return super.decrypt(); + } + return decrypt; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java new file mode 100755 index 0000000..59370c3 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/DefaultIntegrationConfiguration.java @@ -0,0 +1,131 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration; + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.integration.decrypt.DecryptConfiguration; +import com.gitee.starblues.utils.Assert; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * 默认的插件集成配置。给非必须配置设置了默认值 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public abstract class DefaultIntegrationConfiguration implements IntegrationConfiguration{ + + public static final String DEFAULT_PLUGIN_REST_PATH_PREFIX = "plugins"; + public static final Boolean DEFAULT_ENABLE_PLUGIN_ID_REST_PATH_PREFIX = Boolean.TRUE; + + private static final String DEFAULT_TEMP_FILE = + new File(System.getProperty("java.io.tmpdir"), "spring-brick-temp").getPath(); + + @Override + public Boolean enable() { + return Boolean.TRUE; + } + + @Override + public List pluginPath() { + List pluginPath = new ArrayList<>(1); + pluginPath.add("~/plugins/"); + return pluginPath; + } + + @Override + public String uploadTempPath(){ + return DEFAULT_TEMP_FILE; + } + + @Override + public String backupPath(){ + return "backupPlugin"; + } + + @Override + public String pluginRestPathPrefix(){ + return DEFAULT_PLUGIN_REST_PATH_PREFIX; + } + + @Override + public Boolean enablePluginIdRestPathPrefix() { + return DEFAULT_ENABLE_PLUGIN_ID_REST_PATH_PREFIX; + } + + @Override + public Set enablePluginIds() { + return Collections.emptySet(); + } + + @Override + public Set disablePluginIds() { + return Collections.emptySet(); + } + + @Override + public List sortInitPluginIds() { + return Collections.emptyList(); + } + + @Override + public String version() { + return Constants.ALLOW_VERSION; + } + + @Override + public Boolean exactVersion() { + return Boolean.FALSE; + } + + @Override + public Boolean pluginSwaggerScan() { + return Boolean.TRUE; + } + + @Override + public Boolean pluginFollowProfile() { + return Boolean.FALSE; + } + + @Override + public Boolean pluginFollowLog() { + return Boolean.FALSE; + } + + @Override + public DecryptConfiguration decrypt() { + DecryptConfiguration decryptConfiguration = new DecryptConfiguration(); + decryptConfiguration.setEnable(false); + return decryptConfiguration; + } + + /** + * 检查配置 + */ + @Override + public void checkConfig(){ + Assert.isNotEmpty(mainPackage(), "插件配置: [plugin.mainPackage] 不能为空"); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java new file mode 100755 index 0000000..ec930b6 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointConfiguration.java @@ -0,0 +1,100 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration; + +import com.gitee.starblues.core.DefaultRealizeProvider; +import com.gitee.starblues.core.RealizeProvider;; +import com.gitee.starblues.core.classloader.CacheMainResourceMatcher; +import com.gitee.starblues.core.classloader.DefaultMainResourceMatcher; +import com.gitee.starblues.core.classloader.MainResourceMatcher; +import com.gitee.starblues.core.descriptor.decrypt.DefaultPluginDescriptorDecrypt; +import com.gitee.starblues.core.descriptor.decrypt.PluginDescriptorDecrypt; +import com.gitee.starblues.core.launcher.plugin.BasicMainResourcePatternDefiner; +import com.gitee.starblues.core.launcher.plugin.DefaultMainResourcePatternDefiner; +import com.gitee.starblues.integration.operator.DefaultPluginOperator; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.operator.PluginOperatorWrapper; +import com.gitee.starblues.integration.user.DefaultPluginUser; +import com.gitee.starblues.integration.user.PluginUser; +import com.gitee.starblues.spring.extract.ExtractFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 系统Bean配置 + * @author starBlues + * @version 3.0.3 + */ +public class ExtendPointConfiguration { + + private final GenericApplicationContext applicationContext; + private final IntegrationConfiguration configuration; + + public ExtendPointConfiguration(GenericApplicationContext applicationContext, + IntegrationConfiguration configuration) { + this.applicationContext = applicationContext; + this.configuration = configuration; + this.configuration.checkConfig(); + } + + @Bean + @ConditionalOnMissingBean + public PluginUser createPluginUser() { + return new DefaultPluginUser(applicationContext); + } + + @Bean + @ConditionalOnMissingBean + public PluginOperator createPluginOperator(RealizeProvider realizeProvider) { + PluginOperator pluginOperator = new DefaultPluginOperator( + applicationContext, + realizeProvider, + configuration + ); + return new PluginOperatorWrapper(pluginOperator, configuration); + } + + @Bean + @ConditionalOnMissingBean + public RealizeProvider realizeProvider() { + DefaultRealizeProvider defaultRealizeProvider = new DefaultRealizeProvider(configuration, applicationContext); + defaultRealizeProvider.init(); + return defaultRealizeProvider; + } + + @Bean + public ExtractFactory extractFactory(){ + return ExtractFactory.getInstant(); + } + + @Bean + public MainResourceMatcher mainResourceMatcher(){ + return new DefaultMainResourceMatcher(new DefaultMainResourcePatternDefiner( + configuration, + applicationContext + )); + } + + @Bean + @ConditionalOnMissingBean + public PluginDescriptorDecrypt pluginDescriptorDecrypt(){ + return new DefaultPluginDescriptorDecrypt(applicationContext, configuration); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java new file mode 100755 index 0000000..ae20f2d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java @@ -0,0 +1,63 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration; + +import com.gitee.starblues.spring.ResolvePluginThreadClassLoader; +import com.gitee.starblues.spring.web.PluginStaticResourceConfig; +import com.gitee.starblues.spring.web.PluginStaticResourceWebMvcConfigurer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.ResourceResolver; + +/** + * 系统web环境配置点 + * @author starBlues + * @version 3.0.0 + */ +@ConditionalOnWebApplication +@Import({ + ExtendPointWebConfiguration.PluginStaticResourceConfiguration.class, +}) +public class ExtendPointWebConfiguration { + + + @ConditionalOnClass(ResourceResolver.class) + public static class PluginStaticResourceConfiguration{ + + @Bean + @ConditionalOnMissingBean + public PluginStaticResourceWebMvcConfigurer pluginWebResourceResolver(PluginStaticResourceConfig resourceConfig){ + return new PluginStaticResourceWebMvcConfigurer(resourceConfig); + } + + @Bean + @ConditionalOnMissingBean + public PluginStaticResourceConfig pluginStaticResourceConfig() { + return new PluginStaticResourceConfig(); + } + } + + @Bean + public WebMvcConfigurer webMvcConfigurer(){ + return new ResolvePluginThreadClassLoader(); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java new file mode 100755 index 0000000..65b4cac --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/IntegrationConfiguration.java @@ -0,0 +1,200 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration; + + +import com.gitee.starblues.common.Constants; +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.integration.decrypt.DecryptConfiguration; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.List; +import java.util.Set; + + +/** + * 插件集成时的配置接口。插件集成的配置接口 + * @author starBlues + * @version 3.0.1 + */ +public interface IntegrationConfiguration { + + /** + * 是否启用该插件框架 + * @return true 启用, false 禁用 + */ + Boolean enable(); + + /** + * 运行环境。运行项目时的模式。分为开发环境(Dev)、生产环境(Prod) + * @return RuntimeMode.DEV、RuntimeMode.PROD + */ + RuntimeMode environment(); + + /** + * 主程序包名 + * @return String + */ + String mainPackage(); + + /** + * 插件的路径。可设置多个插件路径 + * 开发环境建议直接配置为插件模块的父级目录。例如: plugins。如果启动主程序时, 插件为加载, 请检查该配置是否正确。 + * @return 插件的路径 + */ + List pluginPath(); + + /** + * 上传插件包存储的临时路径。默认 temp(相对于主程序jar路径)。 + * @return 上传插件的临时保存路径。 + */ + String uploadTempPath(); + + /** + * 插件备份路径。默认 backupPlugin (相对于主程序jar路径)。 + * @return 插件备份路径。 + */ + String backupPath(); + + /** + * 插件 RestController 统一请求的路径前缀 + * @return path + */ + String pluginRestPathPrefix(); + + /** + * 是否启用插件id作为RestController的路径前缀。 + * 如果启用。则路径前缀为 pluginRestPathPrefix() 返回的路径拼接插件id, + * 即为: /pathPrefix/pluginId/** + * @return boolean + */ + Boolean enablePluginIdRestPathPrefix(); + + /** + * 启用的插件id + * @return Set + */ + Set enablePluginIds(); + + /** + * 禁用的插件id, 禁用后系统不会启动该插件 + * 如果禁用所有插件, 则Set集合中返回一个字符: * + * @return Set + */ + Set disablePluginIds(); + + /** + * 设置初始化时插件启动的顺序. + * @return 有顺序的插件id + */ + List sortInitPluginIds(); + + /** + * 当前主程序的版本号, 用于校验插件是否可安装. + * 插件中可通过插件配置信息 requires 来指定可安装的主程序版本 + * @return 系统版本号, 如果为为空或者 0.0.0 表示不校验 + */ + String version(); + + /** + * 设置为true表示插件设置的requires的版本号完全匹配version版本号才可允许插件安装, 即: [requires]=[x.y.z] + * 设置为false表示插件设置的requires的版本号小于等于version值, 插件就可安装, 即[requires]小于等于[x.y.z] + * 默认为false + * @return true or false + */ + Boolean exactVersion(); + + /** + * 是否扫描插件 swagger 接口 + * @return true 启动, false 禁用。默认启用 + */ + Boolean pluginSwaggerScan(); + + /** + * 插件的配置文件 Profile 是否跟随主程序的 Profile 配置动态切换 + * @return true: 跟随, false: 不跟随 + */ + Boolean pluginFollowProfile(); + + /** + * 插件日志打印是否跟随主程序 + * @return true: 跟随, false: 不跟随 + */ + Boolean pluginFollowLog(); + + /** + * 解密配置 + * @return DecryptConfiguration + */ + DecryptConfiguration decrypt(); + + + /** + * 检查配置 + */ + default void checkConfig(){}; + + + /** + * 是否是开发环境 + * @return boolean + */ + default boolean isDev(){ + return environment() == RuntimeMode.DEV; + } + + /** + * 是否是生产环境 + * @return boolean + */ + default boolean isProd(){ + return environment() == RuntimeMode.PROD; + } + + + /** + * 是否被启动 + * @param pluginId 插件id + * @return true: 启用, false: 未启用 + */ + default boolean isEnable(String pluginId){ + if(ObjectUtils.isEmpty(enablePluginIds())){ + return true; + } + if(isDisabled(pluginId)){ + return false; + } + return enablePluginIds().contains(pluginId); + } + + + /** + * 是否被禁用 + * @param pluginId 插件id + * @return true: 禁用, false: 未禁用 + */ + default boolean isDisabled(String pluginId){ + if(ObjectUtils.isEmpty(disablePluginIds())){ + return false; + } + if(disablePluginIds().contains(Constants.DISABLED_ALL_PLUGIN)){ + return true; + } + return disablePluginIds().contains(pluginId); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java new file mode 100755 index 0000000..497f3e7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/SpringBootPluginStarter.java @@ -0,0 +1,36 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration; + +import com.gitee.starblues.integration.application.AutoPluginApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * spring-boot-starter + * + * @author starBlues + * @version 3.0.0 + */ +@EnableConfigurationProperties(AutoIntegrationConfiguration.class) +@ConditionalOnExpression("${" + AutoIntegrationConfiguration.ENABLE_STARTER_KEY + ":true}") +@Import(AutoPluginApplication.class) +public class SpringBootPluginStarter { + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java new file mode 100755 index 0000000..0f8fce9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AbstractPluginApplication.java @@ -0,0 +1,54 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.application; + +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.PluginListener; +import com.gitee.starblues.integration.listener.PluginListenerFactory; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.context.ApplicationContext; + +import java.util.List; + +/** + * 公用的的插件应用 + * + * @author starBlues + * @version 3.0.0 + */ +public abstract class AbstractPluginApplication implements PluginApplication { + + /** + * 子类可通过Application 获取插件定义的配置 + * @param applicationContext applicationContext + * @return IntegrationConfiguration + */ + protected IntegrationConfiguration getConfiguration(ApplicationContext applicationContext){ + IntegrationConfiguration configuration = null; + try { + configuration = applicationContext.getBean(IntegrationConfiguration.class); + } catch (Exception e){ + // no show exception + } + if(configuration == null){ + throw new BeanCreationException("没有发现 Bean, " + + "请在 Spring 容器中将 定义为Bean"); + } + return configuration; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java new file mode 100755 index 0000000..c058969 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/AutoPluginApplication.java @@ -0,0 +1,88 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.application; + +import com.gitee.starblues.integration.ExtendPointConfiguration; +import com.gitee.starblues.integration.ExtendPointWebConfiguration; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.user.PluginUser; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Import; +import org.springframework.context.support.GenericApplicationContext; + +/** + * 自动初始化的 PluginApplication。该PluginApplication 基于 Spring InitializingBean 自动初始化插件。 + * + * @author starBlues + * @version 3.0.0 + */ +@Import({ExtendPointConfiguration.class, ExtendPointWebConfiguration.class}) +public class AutoPluginApplication extends DefaultPluginApplication + implements PluginApplication, ApplicationContextAware, ApplicationListener { + + private ApplicationContext applicationContext; + private PluginInitializerListener pluginInitializerListener; + + + /** + * 设置插件初始化监听器 + * @param pluginInitializerListener 插件监听器 + */ + public void setPluginInitializerListener(PluginInitializerListener pluginInitializerListener) { + this.pluginInitializerListener = pluginInitializerListener; + } + + + @Override + public void initialize(ApplicationContext applicationContext, + PluginInitializerListener listener) { + // 此处不允许手动初始化! + throw new RuntimeException("Cannot be initialized manually"); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + protected void setBeanFactory(GenericApplicationContext applicationContext) { + // 忽略 + } + + /** + * Spring boot bean属性被Set完后调用。会自动初始化插件 + */ + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + super.initialize(applicationContext, pluginInitializerListener); + } + + @Override + public PluginOperator getPluginOperator() { + return createPluginOperator(applicationContext); + } + + @Override + public PluginUser getPluginUser() { + return createPluginUser(applicationContext); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java new file mode 100755 index 0000000..f0346b1 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/DefaultPluginApplication.java @@ -0,0 +1,171 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.application; + +import com.gitee.starblues.annotation.Extract; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.operator.PluginOperatorWrapper; +import com.gitee.starblues.integration.user.PluginUser; +import com.gitee.starblues.spring.extract.DefaultExtractFactory; +import com.gitee.starblues.spring.extract.ExtractFactory; +import com.gitee.starblues.spring.extract.OpExtractFactory; +import com.gitee.starblues.utils.ObjectUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + + +/** + * 默认的插件 PluginApplication + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginApplication extends AbstractPluginApplication { + + private final static Logger LOG = LoggerFactory.getLogger(DefaultPluginApplication.class); + + private PluginUser pluginUser; + private PluginOperator pluginOperator; + + private final AtomicBoolean beInitialized = new AtomicBoolean(false); + + public DefaultPluginApplication() { + } + + @Override + public synchronized void initialize(ApplicationContext applicationContext, + PluginInitializerListener listener) { + Objects.requireNonNull(applicationContext, "ApplicationContext can't be null"); + if(beInitialized.get()){ + throw new RuntimeException("Plugin has been initialized"); + } + // 获取Configuration + IntegrationConfiguration configuration = getConfiguration(applicationContext); + // 检查配置 + configuration.checkConfig(); + createPluginUser(applicationContext); + createPluginOperator(applicationContext); + try { + if(!(pluginOperator instanceof PluginOperatorWrapper)){ + pluginOperator = new PluginOperatorWrapper(pluginOperator, configuration); + } + GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; + setBeanFactory(genericApplicationContext); + initExtractFactory(genericApplicationContext); + pluginOperator.initPlugins(listener); + beInitialized.set(true); + } catch (Exception e) { + LOG.error("初始化插件异常." + e.getMessage()); + } + } + + /** + * 创建插件使用者。子类可扩展 + * @param applicationContext Spring ApplicationContext + * @return pluginUser + */ + protected synchronized PluginUser createPluginUser(ApplicationContext applicationContext){ + if(pluginUser == null){ + pluginUser = applicationContext.getBean(PluginUser.class); + } + return pluginUser; + } + + /** + * 创建插件操作者。子类可扩展 + * @param applicationContext Spring ApplicationContext + * @return pluginOperator + */ + protected synchronized PluginOperator createPluginOperator(ApplicationContext applicationContext){ + if(pluginOperator == null){ + pluginOperator = applicationContext.getBean(PluginOperator.class); + } + return pluginOperator; + } + + @Override + public PluginOperator getPluginOperator() { + assertInjected(); + return pluginOperator; + } + + @Override + public PluginUser getPluginUser() { + assertInjected(); + return pluginUser; + } + + /** + * 初始化扩展工厂 + * @param applicationContext applicationContext + */ + private void initExtractFactory(GenericApplicationContext applicationContext){ + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + DefaultExtractFactory defaultExtractFactory = (DefaultExtractFactory)ExtractFactory.getInstant(); + initMainExtract((OpExtractFactory)defaultExtractFactory.getTarget(), beanFactory); + } + + /** + * 初始化主程序中的扩展 + * @param opExtractFactory opExtractFactory + * @param beanFactory beanFactory + */ + private void initMainExtract(OpExtractFactory opExtractFactory, ListableBeanFactory beanFactory){ + // 获取主程序的扩展 + Map extractMap = beanFactory.getBeansWithAnnotation(Extract.class); + if(ObjectUtils.isEmpty(extractMap)){ + return; + } + for (Object extract : extractMap.values()) { + opExtractFactory.addOfMain(extract); + } + } + + /** + * 直接将 PluginOperator 和 PluginUser 注入到ApplicationContext容器中 + * @param applicationContext ApplicationContext + */ + protected void setBeanFactory(GenericApplicationContext applicationContext){ + DefaultListableBeanFactory defaultListableBeanFactory = applicationContext.getDefaultListableBeanFactory(); + defaultListableBeanFactory.registerSingleton(pluginOperator.getClass().getName(), pluginOperator); + defaultListableBeanFactory.registerSingleton(pluginUser.getClass().getName(), pluginUser); + } + + /** + * 检查注入 + */ + private void assertInjected() { + if (this.pluginUser == null) { + throw new RuntimeException("PluginUser is null, Please check whether the DefaultPluginApplication is injected"); + } + if (this.pluginOperator == null) { + throw new RuntimeException("PluginOperator is null, Please check whether the DefaultPluginApplication is injected"); + } + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java new file mode 100755 index 0000000..38d5194 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/application/PluginApplication.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.application; + +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.user.PluginUser; +import org.springframework.context.ApplicationContext; + +/** + * 插件应用。 + * @author starBlues + * @version 3.0.0 + */ +public interface PluginApplication{ + + /** + * 初始化 + * @param applicationContext Spring上下文 + * @param listener 插件初始化监听者 + */ + void initialize(ApplicationContext applicationContext, PluginInitializerListener listener); + + + /** + * 获得插插件操作者 + * @return 插件操作者 + */ + PluginOperator getPluginOperator(); + + /** + * 获得插插件操作者 + * @return 插件操作者 + */ + PluginUser getPluginUser(); +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptConfiguration.java new file mode 100755 index 0000000..eafb1b8 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptConfiguration.java @@ -0,0 +1,61 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.decrypt; + +import com.gitee.starblues.common.cipher.AesPluginCipher; +import com.gitee.starblues.common.cipher.RsaPluginCipher; +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 对插件启动时进行解密校验配置。默认为不启用 + * + * @author starBlues + * @version 3.0.1 + */ +@Data +public class DecryptConfiguration { + + /** + * 是否启动. 默认启用 + */ + private Boolean enable = true; + + /** + * 加解密实现类名称. + * 通过类加载器加载, 然后从Spring容器中获取, 获取不到, 对其直接实例化 + * 默认: {@link AesPluginCipher} + * 可选: + * @see com.gitee.starblues.common.cipher.AesPluginCipher + * @see com.gitee.starblues.common.cipher.RsaPluginCipher + */ + private String className = AesPluginCipher.class.getName(); + + /** + * 总配置 + */ + private Map props = new HashMap<>(); + + /** + * 指定可生效的插件. 如果不配置, 则默认对全部插件生效 + */ + private List plugins; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptPluginConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptPluginConfiguration.java new file mode 100755 index 0000000..d77e26c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/decrypt/DecryptPluginConfiguration.java @@ -0,0 +1,43 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.decrypt; + +import lombok.Data; + +import java.util.Map; + +/** + * 解密插件配置 + * + * @author starBlues + * @version 3.0.1 + */ +@Data +public class DecryptPluginConfiguration { + + /** + * 插件id + */ + private String pluginId; + + /** + * 对当前插件特定的配置, 可以覆盖 {@link DecryptConfiguration#getProps()} 配置 + */ + private Map props; + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java new file mode 100644 index 0000000..054015b --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.listener; + +import org.springframework.context.ApplicationContext; + +/** + * 默认的初始化监听者。内置注册 + * + * @author starBlues + * @version 3.0.0 + */ +public class DefaultInitializerListener implements PluginInitializerListener{ + + + public DefaultInitializerListener(ApplicationContext applicationContext) { + } + + + @Override + public void before() { + + } + + @Override + public void complete() { + refresh(); + } + + @Override + public void failure(Throwable throwable) { + refresh(); + } + + private void refresh(){ + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java new file mode 100644 index 0000000..78bdbb3 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultPluginListenerFactory.java @@ -0,0 +1,142 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.listener; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.ApplicationContext; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * 默认的插件工厂 + * @author starBlues + * @since 3.0.0 + * @version 3.0.3 + */ +public class DefaultPluginListenerFactory implements PluginListenerFactory{ + + private final List listeners; + + public DefaultPluginListenerFactory(ApplicationContext applicationContext){ + listeners = new ArrayList<>(); + addExtendPluginListener(applicationContext); + } + + public DefaultPluginListenerFactory(){ + listeners = new ArrayList<>(); + } + + + private void addExtendPluginListener(ApplicationContext applicationContext){ + List pluginListeners = SpringBeanUtils.getBeans(applicationContext, PluginListener.class); + listeners.addAll(pluginListeners); + } + + @Override + public synchronized void addPluginListener(PluginListener pluginListener) { + if(pluginListener != null){ + listeners.add(pluginListener); + } + } + + @Override + public List getListeners() { + return listeners; + } + + @Override + public void loadSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.loadSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void loadFailure(Path path, Throwable throwable) { + for (PluginListener listener : listeners) { + try { + listener.loadFailure(path, throwable); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void unLoadSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.unLoadSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void startSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.startSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void startFailure(PluginInfo pluginInfo, Throwable throwable) { + for (PluginListener listener : listeners) { + try { + listener.startFailure(pluginInfo, throwable); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void stopSuccess(PluginInfo pluginInfo) { + for (PluginListener listener : listeners) { + try { + listener.stopSuccess(pluginInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public void stopFailure(PluginInfo pluginInfo, Throwable throwable) { + for (PluginListener listener : listeners) { + try { + listener.stopFailure(pluginInfo, throwable); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java new file mode 100644 index 0000000..134e115 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListener.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.listener; + + +/** + * 插件初始化监听者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface PluginInitializerListener { + + /** + * 初始化之前 + */ + void before(); + + + /** + * 初始化完成 + */ + void complete(); + + /** + * 初始化失败 + * @param throwable 异常 + */ + void failure(Throwable throwable); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java new file mode 100644 index 0000000..677d955 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginInitializerListenerFactory.java @@ -0,0 +1,93 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.listener; + +import com.gitee.starblues.utils.SpringBeanUtils; +import org.springframework.context.ApplicationContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * 插件初始化监听者工厂 + * + * @author starBlues + * @version 3.0.0 + */ +public class PluginInitializerListenerFactory implements PluginInitializerListener { + + private final List pluginInitializerListeners = new ArrayList<>(); + + public final ApplicationContext applicationContext; + + public PluginInitializerListenerFactory(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + // 添加默认的初始化监听者 + pluginInitializerListeners.add(new DefaultInitializerListener(applicationContext)); + addExtendPluginListener(applicationContext); + } + + private void addExtendPluginListener(ApplicationContext applicationContext){ + List initializerListeners = SpringBeanUtils.getBeans(applicationContext, + PluginInitializerListener.class); + pluginInitializerListeners.addAll(initializerListeners); + } + + @Override + public void before() { + try { + for (PluginInitializerListener pluginInitializerListener : pluginInitializerListeners) { + pluginInitializerListener.before(); + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + @Override + public void complete() { + try { + for (PluginInitializerListener pluginInitializerListener : pluginInitializerListeners) { + pluginInitializerListener.complete(); + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + @Override + public void failure(Throwable throwable) { + try { + for (PluginInitializerListener pluginInitializerListener : pluginInitializerListeners) { + pluginInitializerListener.failure(throwable); + } + } catch (Exception e){ + e.printStackTrace(); + } + } + + /** + * 添加监听者 + * @param pluginInitializerListener pluginInitializerListener + */ + public void addListener(PluginInitializerListener pluginInitializerListener){ + if(pluginInitializerListener != null){ + pluginInitializerListeners.add(pluginInitializerListener); + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java new file mode 100644 index 0000000..bcf9ceb --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListener.java @@ -0,0 +1,86 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.listener; + + +import com.gitee.starblues.core.PluginInfo; + +import java.nio.file.Path; + +/** + * 插件监听者 + * + * @author starBlues + * @version 3.0.0 + */ +public interface PluginListener { + + /** + * 加载插件成功 + * @param pluginInfo 插件信息 + */ + default void loadSuccess(PluginInfo pluginInfo){} + + /** + * 加载失败 + * @param path 要加载的插件路径 + * @param throwable 异常信息 + */ + default void loadFailure(Path path, Throwable throwable){} + + /** + * 卸载插件成功 + * @param pluginInfo 插件信息 + */ + default void unLoadSuccess(PluginInfo pluginInfo){} + + /** + * 卸载失败 + * @param pluginInfo 插件信息 + * @param throwable 异常信息 + */ + default void unLoadFailure(PluginInfo pluginInfo, Throwable throwable){} + + /** + * 注册插件成功 + * @param pluginInfo 插件信息 + */ + default void startSuccess(PluginInfo pluginInfo){} + + + /** + * 启动失败 + * @param pluginInfo 插件信息 + * @param throwable 异常信息 + */ + default void startFailure(PluginInfo pluginInfo, Throwable throwable){} + + /** + * 卸载插件成功 + * @param pluginInfo 插件信息 + */ + default void stopSuccess(PluginInfo pluginInfo){} + + + /** + * 停止失败 + * @param pluginInfo 插件信息 + * @param throwable 异常信息 + */ + default void stopFailure(PluginInfo pluginInfo, Throwable throwable){} + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java new file mode 100644 index 0000000..9beafdc --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/PluginListenerFactory.java @@ -0,0 +1,43 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.listener; + +import java.util.List; + +/** + * 插件监听工厂 + * + * @author starBlues + * @version 3.0.0 + */ +public interface PluginListenerFactory extends PluginListener { + + /** + * 添加监听者 + * + * @param pluginListener 插件监听者 + */ + void addPluginListener(PluginListener pluginListener); + + /** + * 得到监听者 + * + * @return 监听者集合 + */ + List getListeners(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java new file mode 100755 index 0000000..52ee7c8 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/DefaultPluginOperator.java @@ -0,0 +1,461 @@ +/** + * Copyright [2019-Present] [starBlues] + *

+ * Licensed 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 com.gitee.starblues.integration.operator; + +import com.gitee.starblues.core.*; +import com.gitee.starblues.core.descriptor.InsidePluginDescriptor; +import com.gitee.starblues.core.descriptor.PluginType; +import com.gitee.starblues.core.exception.PluginDisabledException; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.listener.PluginInitializerListenerFactory; +import com.gitee.starblues.integration.operator.upload.UploadByInputStreamParam; +import com.gitee.starblues.integration.operator.upload.UploadByMultipartFileParam; +import com.gitee.starblues.integration.operator.upload.UploadParam; +import com.gitee.starblues.loader.launcher.DevelopmentModeSetting; +import com.gitee.starblues.spring.web.PluginStaticResourceConfig; +import com.gitee.starblues.utils.*; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * 默认的插件操作者 + * + * @author starBlues + * @version 3.1.2 + * @since 3.0.0 + */ +public class DefaultPluginOperator implements PluginOperator { + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final static DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + + private static final AtomicBoolean IS_INIT = new AtomicBoolean(false); + + private final GenericApplicationContext applicationContext; + private final IntegrationConfiguration configuration; + + private final PluginManager pluginManager; + private final PluginInitializerListenerFactory pluginInitializerListenerFactory; + + public DefaultPluginOperator(GenericApplicationContext applicationContext, + RealizeProvider realizeProvider, + IntegrationConfiguration configuration) { + this.applicationContext = applicationContext; + this.configuration = configuration; + this.pluginManager = new PluginLauncherManager(realizeProvider, applicationContext, configuration); + this.pluginInitializerListenerFactory = new PluginInitializerListenerFactory(applicationContext); + } + + @Override + public boolean inited() { + return IS_INIT.get(); + } + + @Override + public synchronized boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException { + if (IS_INIT.get()) { + throw new RuntimeException("插件已经被初始化了, 不能再初始化."); + } + try { + log.info("插件加载环境: {}", configuration.environment().toString()); + log.info("插件加载模式: {}", DevelopmentModeSetting.getDevelopmentMode()); + pluginInitializerListenerFactory.addListener(pluginInitializerListener); + List pluginsRoots = pluginManager.getPluginsRoots(); + if (pluginsRoots.isEmpty()) { + return true; + } + initBeforeLogPrint(); + if (!configuration.enable()) { + log.info("插件功能已被禁用!"); + return false; + } + // 开始加载插件 + List pluginInfos = pluginManager.loadPlugins(); + if (ObjectUtils.isEmpty(pluginInfos)) { + return false; + } + // 触发插件初始化监听器 + pluginInitializerListenerFactory.before(); + boolean isFoundException = false; + IS_INIT.set(true); + pluginInitializerListenerFactory.complete(); + log.info("插件初始化完成"); + return true; + } catch (Exception e) { + pluginInitializerListenerFactory.failure(e); + throw e; + } + } + + /** + * 初始化之前日志打印 + */ + private void initBeforeLogPrint() { + List pluginsRoots = pluginManager.getPluginsRoots(); + log.info("开始加载插件, 插件根路径为: \n{}", String.join("\n", pluginsRoots)); + PluginStaticResourceConfig resourceConfig = SpringBeanUtils.getExistBean(applicationContext, + PluginStaticResourceConfig.class); + if (resourceConfig != null) { + resourceConfig.logPathPrefix(); + } + } + + @Override + public boolean verify(Path jarPath) throws PluginException { + return pluginManager.verify(jarPath); + } + + @Override + public PluginInfo parse(Path pluginPath) throws PluginException { + return toPluginInfo(pluginManager.parse(pluginPath)); + } + + @Override + public PluginInfo install(Path jarPath, boolean unpackPlugin) throws PluginException { + return pluginManager.install(jarPath, unpackPlugin); + } + + @Override + public void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException { + uninstallBackup(pluginId, isDelete, isBackup); + } + + @Override + public PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { + return toPluginInfo(pluginManager.load(pluginPath, unpackPlugin)); + } + + @Override + public boolean unload(String pluginId) throws PluginException { + pluginManager.unLoad(pluginId); + return true; + } + + @Override + public boolean start(String pluginId) throws PluginException { + return pluginManager.start(pluginId) != null; + } + + @Override + public boolean stop(String pluginId) throws PluginException { + PluginInfo pluginInfo = pluginManager.getPluginInfo(pluginId); + if (pluginInfo == null) { + throw new PluginException("没有发现插件: " + pluginId); + } + return pluginManager.stop(pluginId) != null; + } + + @Override + public PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException { + Assert.isNotNull(uploadParam, "参数 uploadParam 不能为空"); + try { + if (uploadParam instanceof UploadByInputStreamParam) { + UploadByInputStreamParam param = (UploadByInputStreamParam) uploadParam; + return uploadPlugin(param.getPluginFileName(), param.getInputStream(), + param.isBackOldPlugin(), param.isUnpackPlugin()); + } else if (uploadParam instanceof UploadByMultipartFileParam) { + UploadByMultipartFileParam param = (UploadByMultipartFileParam) uploadParam; + MultipartFile file = param.getPluginMultipartFile(); + return uploadPlugin(file.getOriginalFilename(), file.getInputStream(), + param.isBackOldPlugin(), param.isUnpackPlugin()); + } else { + throw new PluginException("不支持上传参数: " + uploadParam.getClass().getName()); + } + } catch (Exception e) { + throw PluginException.getPluginException(e, () -> new PluginException(e.getMessage(), e)); + } + } + + @Override + public Path backupPlugin(Path backDirPath, String sign) throws PluginException { + if (configuration.isDev()) { + // 开发环境下不备份 + return backDirPath; + } + Objects.requireNonNull(backDirPath); + return operatePluginFile(backDirPath, sign, true, false); + } + + @Override + public Path backupPlugin(String pluginId, String sign) throws PluginException { + if (configuration.isDev()) { + // 开发环境下不备份 + return null; + } + PluginInfo pluginInfo = getPluginInfo(pluginId); + return backupPlugin(Paths.get(pluginInfo.getPluginPath()), sign); + } + + @Override + public List getPluginInfo() { + return pluginManager.getPluginInfos().stream() + .map(PluginInsideInfo::toPluginInfo) + .collect(Collectors.toList()); + } + + @Override + public PluginInfo getPluginInfo(String pluginId) { + PluginInsideInfo pluginInfo = pluginManager.getPluginInfo(pluginId); + if (pluginInfo != null) { + return pluginInfo.toPluginInfo(); + } + return null; + } + + /** + * 卸载插件 + * + * @param pluginId 插件id + * @param isDelete 是否删除插件 + * @param isBackup 是否备份插件 + * @return 如果备份插件, 则返回备份后的插件路径 + */ + protected Path uninstallBackup(String pluginId, boolean isDelete, boolean isBackup) { + PluginInsideInfo pluginInsideInfo = pluginManager.getPluginInfo(pluginId); + if (pluginInsideInfo == null) { + throw new PluginException(pluginId, "没有发现"); + } + pluginManager.uninstall(pluginId); + if (configuration.isDev()) { + log.trace("开发环境对插件文件不备份和删除"); + return null; + } + return operatePluginFile(pluginInsideInfo, "uninstall", isBackup, isDelete); + } + + protected PluginInfo uploadPlugin(String pluginFileName, InputStream inputStream, + boolean isBackOldPlugin, boolean isUnpackPluginFile) throws Exception { + // 获取文件的后缀名 + if (ObjectUtils.isEmpty(pluginFileName)) { + throw new PluginException("上传的插件文件名称不能为空"); + } + //检查文件格式是否合法 + if (!ResourceUtils.isJar(pluginFileName) && !ResourceUtils.isZip(pluginFileName)) { + throw new PluginException("插件文件类型错误, 请上传 jar/zip 类型文件"); + } + + String tempPathString = FilesUtils.joiningFilePath(configuration.uploadTempPath(), pluginFileName); + Path tempFilePath = Paths.get(tempPathString); + File tempFile = PluginFileUtils.createExistFile(tempFilePath); + // 将上传的插件拷贝到临时目录 + try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { + IOUtils.copy(inputStream, outputStream); + } finally { + IOUtils.closeQuietly(inputStream); + } + File unpackPluginFile = tempFile; + try { + // 解析该插件包 + if (isUnpackPluginFile) { + unpackPluginFile = PluginFileUtils.decompressZip(tempFile.toString(), configuration.uploadTempPath()); + } + PluginInfo uploadPluginInfo = pluginManager.scanParse(unpackPluginFile.toPath()); + if (uploadPluginInfo == null) { + Exception exception = new Exception(pluginFileName + " 文件校验失败"); + verifyFailureDelete(tempFilePath, exception); + throw exception; + } + PluginInfo oldPluginInfo = getPluginInfo(uploadPluginInfo.getPluginId()); + PluginInfo pluginInfo = null; + if (oldPluginInfo != null) { + // 判断版本 + Path oldPluginPath = Paths.get(oldPluginInfo.getPluginPath()); + if (isBackOldPlugin) { + // 先备份一次旧插件 + backupPlugin(oldPluginPath, "upload"); + } + // 删除旧插件包 + File oldFile = oldPluginPath.toFile(); + if (oldFile.exists()) { + FileUtils.delete(oldPluginPath.toFile()); + } + // 然后进入更新模式 + pluginInfo = pluginManager.upgrade(unpackPluginFile.toPath(), false); + } else { + // 不存在则进入安装插件模式 + pluginInfo = pluginManager.install(unpackPluginFile.toPath(), false); + } + return pluginInfo; + } catch (Exception e) { + // 出现异常, 删除刚才上传的临时文件 + verifyFailureDelete(tempFilePath, e); + throw e; + } finally { + // 删除解压文件 + FileUtils.deleteQuietly(unpackPluginFile); + } + } + + /** + * 备份 + * + * @param pluginInsideInfo 插件信息 + * @param sign 文件标志 + * @param back 是否备份插件文件 + * @param delete 是否删除插件文件 + * @return 如果为备份则返回备份后的文件,否则返回null + */ + protected Path operatePluginFile(PluginInsideInfo pluginInsideInfo, String sign, boolean back, boolean delete) { + InsidePluginDescriptor pluginDescriptor = pluginInsideInfo.getPluginDescriptor(); + if (configuration.isDev()) { + // 如果是开发环境, 则不进行备份 + return null; + } + PluginType pluginType = pluginDescriptor.getType(); + if (ObjectUtils.equalsElement(pluginType, PluginType.ZIP, PluginType.JAR, PluginType.DIR)) { + return operatePluginFile(Paths.get(pluginInsideInfo.getPluginPath()), sign, back, delete); + } else if (ObjectUtils.equalsElement(pluginType, PluginType.JAR_OUTER, PluginType.ZIP_OUTER)) { + File pluginLibFile = new File(pluginDescriptor.getPluginLibDir()); + File pluginFile = new File(pluginDescriptor.getPluginPath()); + // 判断获取插件包和插件依赖是否在同一个目录,如果在返回父目录 + File parentFile = FilesUtils.sameParent(pluginLibFile, pluginFile); + if (FilesUtils.isChildFile(pluginManager.getPluginsRoots(), parentFile)) { + // 如果当前插件的父目录为当前插件的子目录 + // 为插件存放目录的子目录 + return operatePluginFile(parentFile.toPath(), sign, back, delete); + } else { + // 只备份插件jar包 + return operatePluginFile(pluginFile.toPath(), sign, back, delete); + } + } else { + return operatePluginFile(Paths.get(pluginDescriptor.getPluginPath()), sign, back, delete); + } + } + + /** + * 备份路径 + * + * @param pluginPath 插件文件 + * @param sign 操作标志 + * @param back 是否备份插件文件 + * @param delete 是否删除插件文件 + * @return 如果为备份则返回备份后的文件,否则返回null + */ + private Path operatePluginFile(Path pluginPath, String sign, boolean back, boolean delete) { + if (!back && !delete) { + return null; + } + if (pluginPath == null) { + log.error("{}失败, 没有发现路径", sign); + return null; + } + if (!Files.exists(pluginPath)) { + log.error("{}}失败, 路径不存在: {}", sign, pluginPath); + return null; + } + File sourceFile = pluginPath.toFile(); + try { + Path targetBackPath = null; + if (back) { + touchBackupPath(); + String targetPathStr = configuration.backupPath() + File.separator; + if (!ObjectUtils.isEmpty(sign)) { + targetPathStr = targetPathStr + sign; + } + targetPathStr = targetPathStr + "_" + getNowTimeByFormat() + "_" + sourceFile.getName(); + targetBackPath = Paths.get(targetPathStr); + File targetBackFile = targetBackPath.toFile(); + copyFile(sourceFile, targetBackFile); + log.info("备份插件文件到: {}", targetBackFile.getAbsolutePath()); + } + if (delete) { + if (sourceFile.isFile()) { + FileUtils.delete(sourceFile); + } else { + FileUtils.deleteDirectory(sourceFile); + } + } + return targetBackPath; + } catch (IOException e) { + log.error("{}路径[{}]失败: {}", sign, pluginPath, e.getMessage(), e); + return null; + } + } + + private void copyFile(File sourceFile, File targetFile) throws IOException { + if (sourceFile.isDirectory()) { + FileUtils.copyDirectory(sourceFile, targetFile); + } else if (sourceFile.isFile()) { + FileUtils.copyFile(sourceFile, targetFile); + } + } + + + /** + * 校验文件失败后, 删除临时文件 + * + * @param tempPluginFile 临时文件路径 + * @param e 异常信息 + * @throws Exception Exception + */ + protected void verifyFailureDelete(Path tempPluginFile, Exception e) throws Exception { + try { + Files.deleteIfExists(tempPluginFile); + } catch (IOException e1) { + log.error("删除临时文件失败: {}. {}", tempPluginFile, e.getMessage()); + } + } + + /** + * 获取现在的时间 + * + * @return String + */ + protected String getNowTimeByFormat() { + LocalDateTime localDateTime = LocalDateTime.now(); + return FORMAT.format(localDateTime); + } + + + protected void touchBackupPath() throws IOException { + String backupPath = configuration.backupPath(); + final File file = new File(backupPath); + if (file.exists()) { + return; + } + FileUtils.forceMkdir(file); + } + + private PluginInfo toPluginInfo(PluginInsideInfo pluginInsideInfo) { + if (pluginInsideInfo == null) { + return null; + } + return pluginInsideInfo.toPluginInfo(); + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/EmptyPluginOperator.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/EmptyPluginOperator.java new file mode 100755 index 0000000..3903cf9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/EmptyPluginOperator.java @@ -0,0 +1,94 @@ +package com.gitee.starblues.integration.operator; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.upload.UploadParam; + +import java.nio.file.Path; +import java.util.List; + +/** + * 空操作的 PluginOperator + * + * @author starBlues + * @version 3.1.0 + * @since 3.0.4 + */ +public class EmptyPluginOperator implements PluginOperator{ + + @Override + public boolean inited() { + return false; + } + + @Override + public boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException { + return false; + } + + @Override + public boolean verify(Path pluginPath) throws PluginException { + return false; + } + + @Override + public PluginInfo parse(Path pluginPath) throws PluginException { + return null; + } + + @Override + public PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException { + return null; + } + + @Override + public void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException { + + } + + @Override + public PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { + return null; + } + + @Override + public boolean unload(String pluginId) throws PluginException { + return false; + } + + @Override + public boolean start(String pluginId) throws PluginException { + return false; + } + + @Override + public boolean stop(String pluginId) throws PluginException { + return false; + } + + @Override + public PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException { + return null; + } + + @Override + public Path backupPlugin(Path backDirPath, String sign) throws PluginException { + return null; + } + + @Override + public Path backupPlugin(String pluginId, String sign) throws PluginException { + return null; + } + + @Override + public List getPluginInfo() { + return null; + } + + @Override + public PluginInfo getPluginInfo(String pluginId) { + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java new file mode 100755 index 0000000..f48d256 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperator.java @@ -0,0 +1,175 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.operator; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.upload.UploadByInputStreamParam; +import com.gitee.starblues.integration.operator.upload.UploadByMultipartFileParam; +import com.gitee.starblues.integration.operator.upload.UploadParam; + +import java.nio.file.Path; +import java.util.List; + +/** + * 操作插件的接口 + * @author starBlues + * @version 3.0.0 + * @see DefaultPluginOperator + */ +public interface PluginOperator { + + /** + * 获取插件是否已初始化 + * @return true初始化,false未初始化 + */ + boolean inited(); + + /** + * 初始化插件 [适用于 dev、prod 环境] + * 该方法只能执行一次。因程序启动时已经调用了该方法,在使用时不要再调用该方法。 + * @param pluginInitializerListener 插件初始化监听者 + * @return true初始化成功, false初始化失败 + * @throws PluginException 抛出异常说明初始化失败 + */ + boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException; + + /** + * 校验插件jar包 [适用于 dev、prod 环境] + * @param pluginPath 插件包绝对路径 + * @return true校验成功, false校验失败 + * @throws PluginException 抛出异常说明校验插件包失败 + */ + boolean verify(Path pluginPath) throws PluginException; + + /** + * 解析插件包 [适用于 dev、prod 环境] + * @param pluginPath 插件包路基 + * @return 解析的插件信息 + * @throws PluginException 抛出异常说明解析插件包失败 + */ + PluginInfo parse(Path pluginPath) throws PluginException; + + /** + * 通过路径安装启动插件 [适用于 dev、prod 环境] + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件包. (如果插件包为压缩包时生效)。 + * 压缩包必须符合zip压缩包, 压缩包不允许出现多级目录 + * 建议将插件和依赖直接压缩为一个zip类型的压缩包 + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + */ + PluginInfo install(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 卸载插件 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @param isDelete 卸载后是否删除插件文件. [适用于 prod 环境] + * @param isBackup 删除插件文件前, 是否备份插件文件。备份文件命名规则为;[uninstall][时间]_原jar名.jar [适用于 prod 环境] + * @throws PluginException 异常信息 + */ + void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException; + + /** + * 加载插件, 但不启动 [适用于 dev、prod 环境] + * @param pluginPath 插件路径 + * @param unpackPlugin 是否解压插件包. (如果插件包为压缩包时生效) + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + */ + PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException; + + /** + * 配合load使用. 针对load的插件进行unload [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 成功返回true.不成功抛出异常或者返回false + * @throws PluginException 异常信息 + */ + boolean unload(String pluginId) throws PluginException; + + /** + * 启用插件 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 成功返回true.不成功抛出异常或者返回false + * @throws PluginException 异常信息 + */ + boolean start(String pluginId) throws PluginException; + + + /** + * 停止插件 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 成功: 返回true; 失败: 抛出异常或者返回false + * @throws PluginException 异常信息 + */ + boolean stop(String pluginId) throws PluginException; + + /** + * 上传插件. [适用于 dev、prod 环境] + * dev模式: + * 如果不存在相同插件(插件id不相同), 则正常上传到临时目录, 然后加载、启动。 + * 如果存在相同插件(插件id相同) + * 相同插件在启动状态, 则进入更新模式(满足上传的插件包版本必须大于已启动的插件版本), 系统会自动卸载旧版本, 然后对临时目录中的插件进行安装、启动。 + * 相同插件不在启动状态, 则直接对临时目录中的插件进行安装、启动。 + * prod模式: + * 如果不存在相同插件(插件id不相同), 则正常上传到插件目录, 然后加载、启动。 + * 如果存在相同插件(插件id相同) + * 相同插件在启动状态, 则进入更新模式(满足上传的插件包版本必须大于已启动的插件版本), 系统会自动卸载旧版本, 安装新版本。 + * 相同插件不在启动状态, 则进入覆盖模式, 会对旧插件进行备份(可根据isBackOldPlugin配置不备份), 然后上传新插件包到插件目录, 然后安装、启动。 + * 如果在插件根目录存在同文件名称插件, 系统会抛出异常, 建议重命名插件名称, 再上传。 + * + * @param uploadParam 上传参数. 如果需要解压文件。则压缩包必须符合zip压缩包, 压缩包不允许出现多级目录, 建议将插件和依赖直接压缩为一个zip类型的压缩包 + * @return 成功: 返回插件信息PluginInfo; 失败: 抛出异常或者返回null + * @throws PluginException 异常信息 + * @see UploadByInputStreamParam + * @see UploadByMultipartFileParam + */ + PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException; + + /** + * 通过路径备份插件文件。[适用于 prod 环境] + * @param backDirPath 备份的目录路径 + * @param sign 备份文件的自定义标识 + * @return 备份插件的路径 + * @throws PluginException 异常信息 + */ + Path backupPlugin(Path backDirPath, String sign) throws PluginException; + + /** + * 通过插件id备份插件。 [适用于 prod 环境] + * @param pluginId 插件id + * @param sign 备份文件的自定义标识 + * @return 备份插件的路径 + * @throws PluginException 异常信息 + */ + Path backupPlugin(String pluginId, String sign) throws PluginException; + + /** + * 获取插件信息 [适用于 dev、prod 环境] + * @return 返回插件信息列表 + */ + List getPluginInfo(); + + /** + * 根据插件id获取插件信息 [适用于 dev、prod 环境] + * @param pluginId 插件id + * @return 插件信息 + */ + PluginInfo getPluginInfo(String pluginId); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java new file mode 100755 index 0000000..8050404 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/PluginOperatorWrapper.java @@ -0,0 +1,183 @@ +/** + * Copyright [2019-Present] [starBlues] + *

+ * Licensed 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 com.gitee.starblues.integration.operator; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import com.gitee.starblues.integration.operator.upload.UploadParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +/** + * 插件操作包装者 + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public class PluginOperatorWrapper implements PluginOperator { + + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + private final PluginOperator pluginOperator; + private final IntegrationConfiguration configuration; + + public PluginOperatorWrapper(PluginOperator pluginOperator, + IntegrationConfiguration configuration) { + this.pluginOperator = pluginOperator; + this.configuration = configuration; + } + + @Override + public boolean inited() { + if (isDisable()) { + return false; + } + return pluginOperator.inited(); + } + + @Override + public boolean initPlugins(PluginInitializerListener pluginInitializerListener) throws PluginException { + if (isDisable()) { + return false; + } + return pluginOperator.initPlugins(pluginInitializerListener); + } + + @Override + public boolean verify(Path jarPath) throws PluginException { + if (isDisable()) { + return false; + } + return pluginOperator.verify(jarPath); + } + + @Override + public PluginInfo parse(Path pluginPath) throws PluginException { + if (isDisable()) { + return null; + } + return pluginOperator.parse(pluginPath); + } + + @Override + public PluginInfo install(Path jarPath, boolean unpackPlugin) throws PluginException { + if (isDisable()) { + return null; + } + return pluginOperator.install(jarPath, unpackPlugin); + } + + @Override + public boolean unload(String pluginId) throws PluginException { + if (isDisable()) { + return false; + } + return pluginOperator.unload(pluginId); + } + + @Override + public void uninstall(String pluginId, boolean isDelete, boolean isBackup) throws PluginException { + if (isDisable()) { + return; + } + pluginOperator.uninstall(pluginId, isDelete, isBackup); + } + + @Override + public PluginInfo load(Path pluginPath, boolean unpackPlugin) throws PluginException { + if (isDisable()) { + return null; + } + return pluginOperator.load(pluginPath, unpackPlugin); + } + + @Override + public boolean start(String pluginId) throws PluginException { + if (isDisable()) { + return false; + } + return pluginOperator.start(pluginId); + } + + @Override + public boolean stop(String pluginId) throws PluginException { + if (isDisable()) { + return false; + } + return pluginOperator.stop(pluginId); + } + + @Override + public PluginInfo uploadPlugin(UploadParam uploadParam) throws PluginException { + if (isDisable()) { + return null; + } + return pluginOperator.uploadPlugin(uploadParam); + } + + @Override + public Path backupPlugin(Path backDirPath, String sign) throws PluginException { + if (isDisable()) { + return null; + } + return pluginOperator.backupPlugin(backDirPath, sign); + } + + @Override + public Path backupPlugin(String pluginId, String sign) throws PluginException { + if (isDisable()) { + return null; + } + return pluginOperator.backupPlugin(pluginId, sign); + } + + @Override + public List getPluginInfo() { + if (isDisable()) { + return Collections.emptyList(); + } + return pluginOperator.getPluginInfo(); + } + + @Override + public PluginInfo getPluginInfo(String pluginId) { + if (isDisable()) { + return null; + } + return pluginOperator.getPluginInfo(pluginId); + } + + /** + * 是否被禁用 + * @return true 禁用 + */ + private boolean isDisable() { + if (configuration.enable()) { + return false; + } + // 如果禁用的话, 直接返回 + log.info("插件功能已被禁用!"); + return true; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java new file mode 100755 index 0000000..97b3dd4 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByInputStreamParam.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.operator.upload; + +import com.gitee.starblues.utils.Assert; + +import java.io.InputStream; + +/** + * InputStream 上传插件参数 + * @author starBlues + * @version 3.0.0 + */ +public class UploadByInputStreamParam extends UploadParam{ + + /** + * 插件文件名称 + */ + private final String pluginFileName; + + /** + * 插件输入流 + */ + private final InputStream inputStream; + + public UploadByInputStreamParam(String pluginFileName, InputStream inputStream) { + this.pluginFileName = Assert.isNotEmpty(pluginFileName, "参数pluginFileName不能为空"); + this.inputStream = Assert.isNotNull(inputStream, "参数inputStream不能为空"); + } + + public String getPluginFileName() { + return pluginFileName; + } + + public InputStream getInputStream() { + return inputStream; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java new file mode 100755 index 0000000..5be7fd7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadByMultipartFileParam.java @@ -0,0 +1,38 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.operator.upload; + +import com.gitee.starblues.utils.Assert; +import org.springframework.web.multipart.MultipartFile; + +/** + * 上传插件参数 + * @author starBlues + * @version 3.0.0 + */ +public class UploadByMultipartFileParam extends UploadParam{ + + private final MultipartFile pluginMultipartFile; + + public UploadByMultipartFileParam(MultipartFile pluginMultipartFile) { + this.pluginMultipartFile = Assert.isNotNull(pluginMultipartFile, "参数pluginMultipartFile不能为空"); + } + + public MultipartFile getPluginMultipartFile() { + return pluginMultipartFile; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java new file mode 100755 index 0000000..3288f4d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/operator/upload/UploadParam.java @@ -0,0 +1,99 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.operator.upload; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * + * 上传插件参数 + * @author starBlues + * @version 3.0.0 + */ +public abstract class UploadParam { + + /** + * 上传后是否启动插件. 默认启动 + */ + private boolean isStartPlugin = true; + + /** + * 如果存在旧插件, 是否备份旧插件 + */ + private boolean isBackOldPlugin = true; + + /** + * 是否解压插件文件 + * 压缩包必须符合zip压缩包, 压缩包不允许出现多级目录, 建议将插件和依赖直接压缩为一个zip类型的压缩包 + */ + private boolean isUnpackPlugin = false; + + protected UploadParam(){} + + public static UploadByInputStreamParam byInputStream(String pluginFileName, InputStream inputStream){ + return new UploadByInputStreamParam(pluginFileName, inputStream); + } + + public static UploadByMultipartFileParam byMultipartFile(MultipartFile pluginMultipartFile){ + return new UploadByMultipartFileParam(pluginMultipartFile); + } + + /** + * 设置上传后是否启动插件. 默认 true + * @param isStartPlugin true: 启动, false 不启动 + * @return UploadParam + */ + public UploadParam setStartPlugin(boolean isStartPlugin) { + this.isStartPlugin = isStartPlugin; + return this; + } + + /** + * 设置是否备份旧插件. 默认: true + * @param isBackOldPlugin true: 备份, false 不备份 + * @return UploadParam + */ + public UploadParam setBackOldPlugin(boolean isBackOldPlugin) { + this.isBackOldPlugin = isBackOldPlugin; + return this; + } + + /** + * 设置是否解压插件. 默认: false + * @param isUnpackPlugin true: 解压, false 不解压 + * @return UploadParam + */ + public UploadParam setUnpackPlugin(boolean isUnpackPlugin) { + this.isUnpackPlugin = isUnpackPlugin; + return this; + } + + public boolean isStartPlugin() { + return isStartPlugin; + } + + public boolean isBackOldPlugin() { + return isBackOldPlugin; + } + + public boolean isUnpackPlugin() { + return isUnpackPlugin; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java new file mode 100755 index 0000000..73f9d5d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/BeanWrapper.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.user; + +import java.util.Map; + +/** + * bean包装类 + * @author starBlues + * @version 3.0.0 + */ +public class BeanWrapper { + + /** + * 主程序bean + */ + private final T mainBean; + + /** + * 插件bean. key为插件id + */ + private final Map pluginBean; + + public BeanWrapper(T mainBean, Map pluginBean) { + this.mainBean = mainBean; + this.pluginBean = pluginBean; + } + + public T getMainBean() { + return mainBean; + } + + public Map getPluginBean() { + return pluginBean; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java new file mode 100755 index 0000000..7639555 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/DefaultPluginUser.java @@ -0,0 +1,159 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.user; + +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.core.launcher.plugin.involved.PluginApplicationContextGetter; +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.SpringBeanUtils; +import com.gitee.starblues.utils.SpringBeanCustomUtils; +import org.springframework.context.support.GenericApplicationContext; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * 默认插件使用者 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultPluginUser implements PluginUser{ + + protected final GenericApplicationContext parentApplicationContext; + + public DefaultPluginUser(GenericApplicationContext parentApplicationContext) { + Objects.requireNonNull(parentApplicationContext, "ApplicationContext can't be null"); + this.parentApplicationContext = parentApplicationContext; + } + + @Override + public BeanWrapper> getBeanName(boolean includeMainBeans) { + Set mainBeanSet = new HashSet<>(); + if(includeMainBeans){ + mainBeanSet = SpringBeanUtils.getBeanName(parentApplicationContext); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map> pluginBeanNames = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + pluginBeanNames.put(k, SpringBeanCustomUtils.getBeanName(v)); + }); + return new BeanWrapper<>(mainBeanSet, pluginBeanNames); + } + + @Override + public Set getBeanName(String pluginId) { + return null; + } + + @Override + public BeanWrapper getBean(String name, boolean includeMainBeans){ + Object mainBean = null; + if(includeMainBeans){ + mainBean = SpringBeanUtils.getExistBean(parentApplicationContext, name); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map pluginBeans = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + Object existBean = SpringBeanCustomUtils.getExistBean(v, name); + if(existBean != null){ + pluginBeans.put(k, existBean); + } + }); + return new BeanWrapper<>(mainBean, pluginBeans); + } + + @Override + public Object getBean(String pluginId, String name) { + ApplicationContext applicationContext = PluginApplicationContextGetter.get(pluginId); + if(applicationContext == null){ + return null; + } + return SpringBeanCustomUtils.getExistBean(applicationContext, name); + } + + @Override + public BeanWrapper> getBeanByInterface(Class interfaceClass, boolean includeMainBeans) { + checkInterface(interfaceClass); + List mainBeans = new ArrayList<>(); + if(includeMainBeans){ + mainBeans = SpringBeanUtils.getBeans(parentApplicationContext, interfaceClass); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map> pluginBeans = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + List beans = SpringBeanCustomUtils.getBeans(v, interfaceClass); + if(!ObjectUtils.isEmpty(beans)){ + pluginBeans.put(k, beans); + } + }); + return new BeanWrapper<>(mainBeans, pluginBeans); + } + + @Override + public List getBeanByInterface(String pluginId, Class interfaceClass) { + checkInterface(interfaceClass); + List result = new ArrayList<>(); + ApplicationContext applicationContext = PluginApplicationContextGetter.get(pluginId); + if(applicationContext != null){ + result.addAll(SpringBeanCustomUtils.getBeans(applicationContext, interfaceClass)); + } + return result; + } + + @Override + public BeanWrapper> getBeansWithAnnotation(Class annotationType, + boolean includeMainBeans) { + List mainBeans = new ArrayList<>(); + if(includeMainBeans){ + mainBeans = SpringBeanUtils.getBeansWithAnnotation(parentApplicationContext, annotationType); + } + Map applicationContexts = PluginApplicationContextGetter.get(); + Map> pluginBeans = new HashMap<>(applicationContexts.size()); + applicationContexts.forEach((k,v)->{ + List beans = SpringBeanCustomUtils.getBeansWithAnnotation(v, annotationType); + if(!ObjectUtils.isEmpty(beans)){ + pluginBeans.put(k, beans); + } + }); + return new BeanWrapper<>(mainBeans, pluginBeans); + } + + + @Override + public List getBeansWithAnnotation(String pluginId, Class annotationType) { + ApplicationContext applicationContext = PluginApplicationContextGetter.get(pluginId); + if(applicationContext != null){ + return SpringBeanCustomUtils.getBeansWithAnnotation(applicationContext, annotationType); + } + return new ArrayList<>(0); + } + + /** + * 判断clazz是否是接口 + * @param clazz clazz + */ + private void checkInterface(Class clazz) { + int modifiers = clazz.getModifiers(); + if (Modifier.isInterface(modifiers)|| Modifier.isAbstract(clazz.getModifiers())) { + return; + } + throw new PluginException("[" + clazz.getName() + "]不是一个接口"); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java new file mode 100755 index 0000000..c786e1a --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/user/PluginUser.java @@ -0,0 +1,98 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.integration.user; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 该接口用于在主程序操作获取主程序/插件中 Spring 管理 Bean + * @author starBlues + * @version 3.0.0 + */ +public interface PluginUser { + + /** + * 获取 Bean名称 + * @param includeMainBeans 是否包含主程序 Bean + * @return Bean 包装对象 + */ + BeanWrapper> getBeanName(boolean includeMainBeans); + + /** + * 获取插件中的Bean名称 + * @param pluginId 插件id + * @return Bean名称集合 + */ + Set getBeanName(String pluginId); + + + /** + * 通过 Bean名称获取 Bean 对象。 + * @param name Bean的名称。 + * @param includeMainBeans 是否包含主程序 Bean + * @return Bean包装对象 + */ + BeanWrapper getBean(String name, boolean includeMainBeans); + + /** + * 通过 Bean名称获取具体插件中的 Bean 对象 + * @param pluginId 插件id。 + * @param name Bean名称 + * @return Object + */ + Object getBean(String pluginId, String name); + + + /** + * 通过接口获取实现的对象集合 + * @param interfaceClass 接口的类 + * @param includeMainBeans 是否包含主程序 Bean + * @param Bean的类型 + * @return Bean包装对象 + */ + BeanWrapper> getBeanByInterface(Class interfaceClass, boolean includeMainBeans); + + /** + * 通过接口获取具体插件中的实现对象集合 + * @param pluginId 插件id + * @param interfaceClass 接口的类 + * @param Bean的类型 + * @return List + */ + List getBeanByInterface(String pluginId, Class interfaceClass); + + /** + * 通过注解获取 Bean + * @param annotationType 注解类型 + * @param includeMainBeans 是否包含主程序 Bean + * @return Bean包装对象 + */ + BeanWrapper> getBeansWithAnnotation(Class annotationType, boolean includeMainBeans); + + /** + * 通过注解获取具体插件中的 Bean + * @param pluginId 插件id + * @param annotationType 注解类型 + * @return 该注解的 Bean 集合 + */ + List getBeansWithAnnotation(String pluginId, Class annotationType); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java new file mode 100755 index 0000000..e9ad336 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContext.java @@ -0,0 +1,39 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +/** + * 自定义ApplicationContext + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public interface ApplicationContext extends AutoCloseable { + + /** + * 得到 SpringBeanFactory + * @return SpringBeanFactory + */ + SpringBeanFactory getSpringBeanFactory(); + + /** + * 得到原始的BeanFactory + * @return BeanFactory + */ + Object getSourceBeanFactory(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java new file mode 100755 index 0000000..1330a08 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ApplicationContextProxy.java @@ -0,0 +1,42 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +/** + * ApplicationContext 代理 + * @author starBlues + * @version 3.0.0 + */ +public class ApplicationContextProxy extends GenericApplicationContext{ + + public ApplicationContextProxy(Object targetBeanFactory, + AutoCloseable autoCloseable) { + super(autoCloseable); + setSpringBeanFactory(createSpringBeanFactory(targetBeanFactory)); + } + + public ApplicationContextProxy(Object targetBeanFactory) { + super(); + setSpringBeanFactory(createSpringBeanFactory(targetBeanFactory)); + setSourcesBeanFactory(targetBeanFactory); + } + + protected SpringBeanFactory createSpringBeanFactory(Object targetBeanFactory){ + ProxyFactory proxyFactory = new CacheJdkSameTypeParamProxyFactory(targetBeanFactory); + return proxyFactory.getObject(SpringBeanFactory.class); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java new file mode 100755 index 0000000..910c9f5 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/CacheJdkSameTypeParamProxyFactory.java @@ -0,0 +1,59 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import com.gitee.starblues.utils.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 可缓存的代理工厂 + * @author starBlues + * @version 3.0.0 + */ +public class CacheJdkSameTypeParamProxyFactory extends JdkSameTypeParamProxyFactory{ + + private final Map methodCache = new ConcurrentHashMap<>(); + + public CacheJdkSameTypeParamProxyFactory(Object target) { + super(target); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method targetMethod = methodCache.get(method); + if(targetMethod == null){ + Class[] paramTypes = null; + if(args != null){ + paramTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + paramTypes[i] = args[i].getClass(); + } + } + targetMethod = ReflectionUtils.findMethod(target.getClass(), method.getName(), paramTypes); + if(targetMethod != null){ + methodCache.put(method, targetMethod); + } else { + throw ReflectionUtils.getNoSuchMethodException(target.getClass(), method.getName(), paramTypes); + } + } + return targetMethod.invoke(target, args); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java new file mode 100755 index 0000000..6dd021b --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/GenericApplicationContext.java @@ -0,0 +1,66 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import com.gitee.starblues.utils.Assert; + +/** + * 基本的ApplicationContext + * @author starBlues + * @version 3.0.0 + */ +public class GenericApplicationContext implements ApplicationContext{ + + public final AutoCloseable autoCloseable; + + protected SpringBeanFactory springBeanFactory; + protected Object sourcesBeanFactory; + + public GenericApplicationContext() { + this(null); + } + + public GenericApplicationContext(AutoCloseable autoCloseable) { + this.autoCloseable = autoCloseable; + } + + public void setSpringBeanFactory(SpringBeanFactory springBeanFactory){ + this.springBeanFactory = Assert.isNotNull(springBeanFactory, "参数 springBeanFactory 不能为空"); + } + + public void setSourcesBeanFactory(Object sourcesBeanFactory) { + this.sourcesBeanFactory = sourcesBeanFactory; + } + + @Override + public SpringBeanFactory getSpringBeanFactory() { + return springBeanFactory; + } + + @Override + public Object getSourceBeanFactory() { + return sourcesBeanFactory; + } + + + @Override + public void close() throws Exception { + if(autoCloseable != null){ + autoCloseable.close(); + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java new file mode 100755 index 0000000..ffef67f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/JdkSameTypeParamProxyFactory.java @@ -0,0 +1,51 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import com.gitee.starblues.utils.ReflectionUtils; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * jdk 同类型参数的代理工厂 + * @author starBlues + * @version 3.0.0 + */ +public class JdkSameTypeParamProxyFactory implements ProxyFactory, InvocationHandler { + + protected final Object target; + + public JdkSameTypeParamProxyFactory(Object target) { + this.target = target; + } + + @SuppressWarnings("unchecked") + @Override + public T getObject(Class interfacesClass) { + ClassLoader classLoader = interfacesClass.getClassLoader(); + Class[] interfaces = new Class[]{ interfacesClass }; + return (T) Proxy.newProxyInstance(classLoader, interfaces, this); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return ReflectionUtils.invoke(target, method.getName(), args); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java new file mode 100755 index 0000000..b121ca7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContext.java @@ -0,0 +1,97 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import com.gitee.starblues.spring.environment.EnvironmentProvider; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.Map; + +/** + * 主程序 ApplicationContext 接口 + * @author starBlues + * @version 3.0.1 + */ +public interface MainApplicationContext extends ApplicationContext { + + /** + * 得到主程序所有配置的 env + * + * @return 主程序配置的 env 集合 + */ + Map> getConfigurableEnvironment(); + + /** + * 得到主程序配置的 Provider + * @return EnvironmentProvider + */ + EnvironmentProvider getEnvironmentProvider(); + + /** + * 返回主程序配置的Profile配置 + * @return String 数组 + */ + String[] getActiveProfiles(); + + /** + * 返回主程序默认的Profile配置 + * @return String 数组 + */ + String[] getDefaultProfiles(); + + /** + * 从主程序获取依赖 + * + * @param requestingBeanName 依赖Bean名称 + * @param dependencyType 依赖类型 + * @return boolean + */ + Object resolveDependency(String requestingBeanName, Class dependencyType); + + /** + * 是否为web环境 + * @return boolean + */ + boolean isWebEnvironment(); + + /** + * 得到原始的 ApplicationContext + * @return Object + */ + Object getSourceApplicationContext(); + + /** + * 是否能注册Controller + * @return boolean + */ + boolean isRegisterController(); + + /** + * 获取主程序的 RequestMappingHandlerMapping + * @return RequestMappingHandlerMapping + */ + RequestMappingHandlerMapping getRequestMappingHandlerMapping(); + + /** + * 获取主程序的 RequestMappingHandlerAdapter + * @return RequestMappingHandlerAdapter + */ + RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java new file mode 100755 index 0000000..96f7b58 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/MainApplicationContextProxy.java @@ -0,0 +1,162 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import com.gitee.starblues.spring.environment.EnvironmentProvider; +import com.gitee.starblues.spring.environment.MainSpringBootEnvironmentProvider; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.SpringBeanUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.*; + +/** + * 主程序 ApplicationContext 的实现 + * @author starBlues + * @version 3.0.1 + */ +@Slf4j +public class MainApplicationContextProxy extends ApplicationContextProxy implements MainApplicationContext{ + + private final static String REQUEST_MAPPING_BANE_NAME = "requestMappingHandlerMapping"; + private final static String REQUEST_MAPPING_ADAPTER_BANE_NAME = "requestMappingHandlerAdapter"; + + private final GenericApplicationContext applicationContext; + private final boolean isWebEnvironment; + + private final boolean isRegisterController; + private final RequestMappingHandlerMapping requestMappingHandlerMapping; + private final RequestMappingHandlerAdapter requestMappingHandlerAdapter; + + public MainApplicationContextProxy(GenericApplicationContext applicationContext) { + this(applicationContext, null); + } + + public MainApplicationContextProxy(GenericApplicationContext applicationContext, + AutoCloseable autoCloseable) { + super(applicationContext.getBeanFactory(), autoCloseable); + this.applicationContext = applicationContext; + this.isWebEnvironment = getIsWebEnvironment(applicationContext); + if(isWebEnvironment){ + this.requestMappingHandlerMapping = SpringBeanUtils.getExistBean( + applicationContext, REQUEST_MAPPING_BANE_NAME, RequestMappingHandlerMapping.class); + this.requestMappingHandlerAdapter = SpringBeanUtils.getExistBean( + applicationContext, REQUEST_MAPPING_ADAPTER_BANE_NAME, RequestMappingHandlerAdapter.class); + if(this.requestMappingHandlerMapping == null || this.requestMappingHandlerAdapter == null){ + log.error("主程序环境异常, 插件不能注册 Controller 接口!"); + isRegisterController = false; + } else { + isRegisterController = true; + } + } else { + this.isRegisterController = false; + this.requestMappingHandlerMapping = null; + this.requestMappingHandlerAdapter = null; + } + } + + @Override + public Map> getConfigurableEnvironment() { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + Map> environmentMap = new LinkedHashMap<>(propertySources.size()); + for (PropertySource propertySource : propertySources) { + if (!(propertySource instanceof EnumerablePropertySource)) { + continue; + } + EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; + String[] propertyNames = enumerablePropertySource.getPropertyNames(); + Map values = new HashMap<>(propertyNames.length); + for (String propertyName : propertyNames) { + values.put(propertyName, enumerablePropertySource.getProperty(propertyName)); + } + if (!values.isEmpty()) { + environmentMap.put(propertySource.getName(), values); + } + } + return environmentMap; + } + + @Override + public EnvironmentProvider getEnvironmentProvider() { + return new MainSpringBootEnvironmentProvider(applicationContext.getEnvironment()); + } + + @Override + public String[] getActiveProfiles() { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + @Override + public String[] getDefaultProfiles() { + return applicationContext.getEnvironment().getDefaultProfiles(); + } + + @Override + public Object resolveDependency(String requestingBeanName, Class dependencyType) { + try { + return applicationContext.getBean(dependencyType); + } catch (Exception e){ + return null; + } + } + + @Override + public boolean isWebEnvironment() { + return isWebEnvironment; + } + + @Override + public Object getSourceApplicationContext() { + return applicationContext; + } + + @Override + public boolean isRegisterController() { + return isRegisterController; + } + + @Override + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + return requestMappingHandlerMapping; + } + + @Override + public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() { + return requestMappingHandlerAdapter; + } + + @Override + public Object getSourceBeanFactory() { + return applicationContext.getBeanFactory(); + } + + private boolean getIsWebEnvironment(GenericApplicationContext applicationContext){ + return applicationContext instanceof AnnotationConfigServletWebServerApplicationContext + || applicationContext instanceof AnnotationConfigReactiveWebServerApplicationContext; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java new file mode 100755 index 0000000..e2c9db3 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ProxyFactory.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +/** + * 代理工厂 + * @author starBlues + * @version 3.0.0 + */ +public interface ProxyFactory { + + /** + * 获取代理类 + * @param interfacesClass 需代理的接口 + * @param 代理实现的泛型 + * @return 代理实现 + */ + T getObject(Class interfacesClass); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ResolvePluginThreadClassLoader.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ResolvePluginThreadClassLoader.java new file mode 100755 index 0000000..dfd84e5 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/ResolvePluginThreadClassLoader.java @@ -0,0 +1,71 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import com.gitee.starblues.loader.classloader.GenericClassLoader; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 解决插件线程中的ClassLoader + * + * @author starBlues + * @version 3.0.3 + */ +public class ResolvePluginThreadClassLoader implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new ResolveHandlerInterceptor()).addPathPatterns("/**"); + } + + private static class ResolveHandlerInterceptor implements HandlerInterceptor { + + private final ThreadLocal oldClassLoader = new ThreadLocal<>(); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if(handler instanceof HandlerMethod){ + HandlerMethod handlerMethod = (HandlerMethod) handler; + ClassLoader classLoader = handlerMethod.getBeanType().getClassLoader(); + if(classLoader instanceof GenericClassLoader){ + oldClassLoader.set(Thread.currentThread().getContextClassLoader()); + Thread.currentThread().setContextClassLoader(classLoader); + } + } + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + try { + ClassLoader classLoader = oldClassLoader.get(); + if(classLoader != null){ + Thread.currentThread().setContextClassLoader(classLoader); + } + } finally { + oldClassLoader.remove(); + } + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java new file mode 100755 index 0000000..fed6f7c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringBeanFactory.java @@ -0,0 +1,29 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import org.springframework.beans.factory.ListableBeanFactory; + +/** + * spring bean factory 封装接口 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public interface SpringBeanFactory extends ListableBeanFactory { + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java new file mode 100755 index 0000000..634658f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/SpringPluginHook.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + + +import com.gitee.starblues.core.PluginCloseType; +import com.gitee.starblues.core.exception.PluginProhibitStopException; + +/** + * 插件把柄接口 + * @author starBlues + * @since 3.0.0 + * @version 3.1.0 + */ +public interface SpringPluginHook { + + /** + * 停止前校验. 如果抛出 PluginProhibitStopException 异常, 表示当前插件不可停止 + * @throws PluginProhibitStopException 插件禁止停止 + */ + void stopVerify() throws PluginProhibitStopException; + + /** + * 返回插件 ApplicationContext + * @return ApplicationContext + */ + ApplicationContext getApplicationContext(); + + /** + * 得到插件中对 web 的配置 + * @return WebConfig + */ + WebConfig getWebConfig(); + + /** + * 关闭调用 + * @param closeType 关闭类型 + * @since 3.1.0 + * @throws Exception 关闭异常 + */ + void close(PluginCloseType closeType) throws Exception; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java new file mode 100755 index 0000000..1cc006e --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/WebConfig.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring; + +import lombok.Data; + +import java.util.Set; + +/** + * 插件中对web的配置 + * @author starBlues + * @version 3.0.0 + */ +@Data +public class WebConfig { + + private boolean enable = false; + private Set resourceLocations = null; + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EmptyEnvironmentProvider.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EmptyEnvironmentProvider.java new file mode 100755 index 0000000..a9ed7b7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EmptyEnvironmentProvider.java @@ -0,0 +1,74 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.environment; + +import java.util.function.BiConsumer; + +/** + * 空的配置信息提供者 + * + * @author starBlues + * @version 3.0.3 + */ +public class EmptyEnvironmentProvider implements EnvironmentProvider{ + + + @Override + public Object getValue(String name) { + return null; + } + + @Override + public String getString(String name) { + return null; + } + + @Override + public Integer getInteger(String name) { + return null; + } + + @Override + public Long getLong(String name) { + return null; + } + + @Override + public Double getDouble(String name) { + return null; + } + + @Override + public Float getFloat(String name) { + return null; + } + + @Override + public Boolean getBoolean(String name) { + return null; + } + + @Override + public EnvironmentProvider getByPrefix(String prefix) { + return new EmptyEnvironmentProvider(); + } + + @Override + public void forEach(BiConsumer action) { + + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EnvironmentProvider.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EnvironmentProvider.java new file mode 100755 index 0000000..8ab6be9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/EnvironmentProvider.java @@ -0,0 +1,92 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.environment; + +import java.util.function.BiConsumer; + +/** + * 配置信息提供者接口 + * + * @author starBlues + * @version 3.0.3 + */ +public interface EnvironmentProvider { + + /** + * 根据名称获取配置值 + * @param name 配置名称 + * @return 配置值 + */ + Object getValue(String name); + + /** + * 根据名称获取 String 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + String getString(String name); + + /** + * 根据名称获取 Integer 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Integer getInteger(String name); + + /** + * 根据名称获取 Long 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Long getLong(String name); + + /** + * 根据名称获取 Double 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Double getDouble(String name); + + /** + * 根据名称获取 Float 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Float getFloat(String name); + + /** + * 根据名称获取 Boolean 类型配置值 + * @param name 配置名称 + * @return 配置值 + */ + Boolean getBoolean(String name); + + /** + * 根据前缀名称批量获取配置值 + * @param prefix 前缀 + * @return 环境 + */ + EnvironmentProvider getByPrefix(String prefix); + + /** + * 获取所有配置集合 + * @param action 每个条目执行的操作 + */ + void forEach(BiConsumer action); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MainSpringBootEnvironmentProvider.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MainSpringBootEnvironmentProvider.java new file mode 100755 index 0000000..e126ba7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MainSpringBootEnvironmentProvider.java @@ -0,0 +1,108 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.environment; + +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ObjectValueUtils; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * 主程序配置信息提供者实现 + * + * @author starBlues + * @version 3.0.3 + */ +public class MainSpringBootEnvironmentProvider implements EnvironmentProvider { + + private final ConfigurableEnvironment environment; + + public MainSpringBootEnvironmentProvider(ConfigurableEnvironment environment) { + this.environment = environment; + } + + @Override + public Object getValue(String name) { + MutablePropertySources propertySources = environment.getPropertySources(); + for (PropertySource propertySource : propertySources) { + Object property = propertySource.getProperty(name); + if (property != null) { + return property; + } + } + return null; + } + + @Override + public String getString(String name) { + return ObjectValueUtils.getString(getValue(name)); + } + + @Override + public Integer getInteger(String name) { + return ObjectValueUtils.getInteger(getValue(name)); + } + + @Override + public Long getLong(String name) { + return ObjectValueUtils.getLong(getValue(name)); + } + + @Override + public Double getDouble(String name) { + return ObjectValueUtils.getDouble(getValue(name)); + } + + @Override + public Float getFloat(String name) { + return ObjectValueUtils.getFloat(getValue(name)); + } + + @Override + public Boolean getBoolean(String name) { + return ObjectValueUtils.getBoolean(getValue(name)); + } + + @Override + public EnvironmentProvider getByPrefix(String prefix) { + if(ObjectUtils.isEmpty(prefix)){ + return new EmptyEnvironmentProvider(); + } + Map collect = new LinkedHashMap<>(); + MutablePropertySources propertySources = environment.getPropertySources(); + for (PropertySource propertySource : propertySources) { + String name = propertySource.getName(); + if(name.startsWith(prefix)){ + collect.put(MapEnvironmentProvider.resolveKey(prefix, name), propertySource.getSource()); + } + } + return new MapEnvironmentProvider(collect); + } + + @Override + public void forEach(BiConsumer action) { + MutablePropertySources propertySources = environment.getPropertySources(); + for (PropertySource propertySource : propertySources) { + action.accept(propertySource.getName(), propertySource.getSource()); + } + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MapEnvironmentProvider.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MapEnvironmentProvider.java new file mode 100755 index 0000000..095462c --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/environment/MapEnvironmentProvider.java @@ -0,0 +1,119 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.environment; + +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.ObjectValueUtils; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * map类型的配置信息提供者 + * + * @author starBlues + * @version 3.0.3 + */ +public class MapEnvironmentProvider implements EnvironmentProvider{ + + private final String prefix; + private final Map source; + + public MapEnvironmentProvider(Map source){ + this(null, source); + } + + public MapEnvironmentProvider(String prefix, Map source) { + if(prefix == null){ + this.prefix = ""; + } else { + this.prefix = prefix; + } + if(ObjectUtils.isEmpty(source)){ + this.source = Collections.emptyMap(); + } else { + this.source = source; + } + } + + public static String resolveKey(String prefix, String name){ + String key = name.replace(prefix, ""); + int i = key.indexOf("."); + if(i > -1){ + key = key.substring(i + 1); + } + return key; + } + + @Override + public Object getValue(String name) { + String key = prefix + name; + return source.get(key); + } + + @Override + public String getString(String name) { + return ObjectValueUtils.getString(getValue(name)); + } + + @Override + public Integer getInteger(String name) { + return ObjectValueUtils.getInteger(getValue(name)); + } + + @Override + public Long getLong(String name) { + return ObjectValueUtils.getLong(getValue(name)); + } + + @Override + public Double getDouble(String name) { + return ObjectValueUtils.getDouble(getValue(name)); + } + + @Override + public Float getFloat(String name) { + return ObjectValueUtils.getFloat(getValue(name)); + } + + @Override + public Boolean getBoolean(String name) { + return ObjectValueUtils.getBoolean(getValue(name)); + } + + @Override + public EnvironmentProvider getByPrefix(String prefix) { + if(ObjectUtils.isEmpty(prefix)){ + return new EmptyEnvironmentProvider(); + } + Map collect = new LinkedHashMap<>(); + source.forEach((k,v)->{ + if(k.startsWith(prefix)){ + collect.put(resolveKey(prefix, k), v); + } + }); + return new MapEnvironmentProvider(collect); + } + + @Override + public void forEach(BiConsumer action) { + source.forEach(action); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java new file mode 100755 index 0000000..93d846f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultExtractFactory.java @@ -0,0 +1,72 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.extract; + +import java.util.*; + +/** + * 默认的扩展工厂 + * @author starBlues + * @version 3.0.0 + */ +public class DefaultExtractFactory implements ExtractFactory{ + + private final ExtractFactory target; + + public DefaultExtractFactory() { + this.target = new DefaultOpExtractFactory(); + } + + public ExtractFactory getTarget() { + return target; + } + + @Override + public T getExtractByCoordinate(ExtractCoordinate coordinate) { + return target.getExtractByCoordinate(coordinate); + } + + @Override + public T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate) { + return target.getExtractByCoordinate(pluginId, coordinate); + } + + @Override + public T getExtractByCoordinateOfMain(ExtractCoordinate coordinate) { + return target.getExtractByCoordinate(coordinate); + } + + @Override + public List getExtractByInterClass(Class interfaceClass) { + return target.getExtractByInterClass(interfaceClass); + } + + @Override + public List getExtractByInterClass(String pluginId, Class interfaceClass) { + return target.getExtractByInterClass(pluginId, interfaceClass); + } + + @Override + public List getExtractByInterClassOfMain(Class interfaceClass) { + return target.getExtractByInterClassOfMain(interfaceClass); + } + + @Override + public Map> getExtractCoordinates() { + return target.getExtractCoordinates(); + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java new file mode 100755 index 0000000..a2a9436 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/DefaultOpExtractFactory.java @@ -0,0 +1,213 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.extract; + +import com.gitee.starblues.annotation.Extract; +import com.gitee.starblues.utils.ObjectUtils; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.ClassUtils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 默认的可扩展的工厂 + * @author starBlues + * @version 3.1.0 + * @since 3.0.0 + */ +public class DefaultOpExtractFactory implements OpExtractFactory { + + public static final String MAIN_EXTRACT_KEY = ExtractFactory.class.getName() + UUID.randomUUID().toString(); + + private final Map> extractMap = new ConcurrentHashMap<>(); + + + @Override + public void addOfMain(Object extractObject) { + add(MAIN_EXTRACT_KEY, extractObject); + } + + @Override + public void add(String pluginId, Object extractObject) { + if(extractObject == null){ + return; + } + Extract extract = getExtract(extractObject); + if(extract == null){ + return; + } + Map extractObjects = extractMap.computeIfAbsent(pluginId, k -> + new ConcurrentHashMap<>()); + ExtractWrapper extractWrapper = new ExtractWrapper(extractObject, extract.order()); + extractObjects.put(new ExtractCoordinate(extract, extractObject.getClass()), extractWrapper); + } + + @Override + public void remove(String pluginId) { + extractMap.remove(pluginId); + } + + @SuppressWarnings("unchecked") + @Override + public T getExtractByCoordinate(ExtractCoordinate coordinate) { + Objects.requireNonNull(coordinate, "ExtractCoordinate can't be null"); + int currentOrder = Integer.MIN_VALUE; + Object currentObject = null; + for (Map value : extractMap.values()) { + ExtractWrapper extractWrapper = value.get(coordinate); + if(extractWrapper != null){ + int order = extractWrapper.getOrder(); + if(order > currentOrder){ + currentObject = extractWrapper.getObject(); + // fix: https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I430R6 + currentOrder = order; + } + } + } + if(currentObject != null){ + return (T) currentObject; + } + throw new RuntimeException("Not found " + coordinate); + } + + @SuppressWarnings("unchecked") + @Override + public T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate) { + Objects.requireNonNull(coordinate, "ExtractCoordinate can't be null"); + Map extractCoordinates = extractMap.get(pluginId); + if(extractCoordinates == null){ + throw new RuntimeException("Not found " + coordinate + " from plugin '" + pluginId + "'"); + } + ExtractWrapper extracts = extractCoordinates.get(coordinate); + if(extracts == null){ + throw new RuntimeException("Not found " + coordinate + " from plugin '" + pluginId + "'"); + } + // fix https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I5IFR4 + return (T) extracts.getObject(); + } + + @SuppressWarnings("unchecked") + @Override + public T getExtractByCoordinateOfMain(ExtractCoordinate coordinate) { + Objects.requireNonNull(coordinate, "ExtractCoordinate can't be null"); + Map extractCoordinates = extractMap.get(MAIN_EXTRACT_KEY); + if(extractCoordinates == null){ + throw new RuntimeException("Not found " + coordinate + " from main"); + } + ExtractWrapper extractWrapper = extractCoordinates.get(coordinate); + if(extractWrapper == null){ + throw new RuntimeException("Not found " + coordinate + " from main"); + } + return (T) extractWrapper.getObject(); + } + + @Override + public List getExtractByInterClass(Class interfaceClass) { + if(interfaceClass == null){ + return Collections.emptyList(); + } + List extracts = new ArrayList<>(); + for (Map value : extractMap.values()) { + for (ExtractWrapper extractWrapper : value.values()) { + Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet( + extractWrapper.getObject().getClass()); + if(allInterfacesForClassAsSet.contains(interfaceClass)){ + extracts.add(extractWrapper); + } + } + } + return sort(extracts); + } + + @Override + public List getExtractByInterClass(String pluginId, Class interfaceClass) { + if(interfaceClass == null){ + return Collections.emptyList(); + } + List extracts = new ArrayList<>(); + Map extractCoordinateObjectMap = extractMap.get(pluginId); + if(extractCoordinateObjectMap == null || extractCoordinateObjectMap.isEmpty()){ + return Collections.emptyList(); + } + for (ExtractWrapper wrapper : extractCoordinateObjectMap.values()) { + Object object = wrapper.getObject(); + Set> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(object.getClass()); + if(allInterfacesForClassAsSet.contains(interfaceClass)){ + extracts.add(wrapper); + } + } + return sort(extracts); + } + + @Override + public List getExtractByInterClassOfMain(Class interfaceClass) { + return getExtractByInterClass(MAIN_EXTRACT_KEY, interfaceClass); + } + + @Override + public Map> getExtractCoordinates() { + Map> extractCoordinateMap = new HashMap<>(extractMap.size()); + extractMap.forEach((k, v)->{ + Set extractCoordinates = new HashSet<>(v.size()); + extractCoordinates.addAll(v.keySet()); + extractCoordinateMap.put(k, extractCoordinates); + }); + return extractCoordinateMap; + } + + @SuppressWarnings("unchecked") + private List sort(List extractWrappers){ + if(ObjectUtils.isEmpty(extractWrappers)){ + return new ArrayList<>(0); + } + return extractWrappers.stream() + .sorted(Comparator.comparing(ExtractWrapper::getOrder, + Comparator.nullsLast(Comparator.reverseOrder()))) + .map(extractWrapper -> (T) extractWrapper.getObject()) + .collect(Collectors.toList()); + } + + private Extract getExtract(Object extractObject){ + return AnnotationUtils.findAnnotation(extractObject.getClass(),Extract.class); + } + + + /** + * 扩展对象包装类型 + **/ + private static class ExtractWrapper{ + private final Object object; + private final int order; + + public ExtractWrapper(Object object, int order) { + this.object = object; + this.order = order; + } + + public Object getObject() { + return object; + } + + public int getOrder() { + return order; + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java new file mode 100755 index 0000000..01755e9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractCoordinate.java @@ -0,0 +1,120 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.extract; + +import com.gitee.starblues.annotation.Extract; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.Objects; + +/** + * 执行器坐标 + * @author starBlues + * @version 3.0.0 + */ +public class ExtractCoordinate { + + private final String bus; + private final String scene; + private final String useCase; + private final Class extractClass; + + ExtractCoordinate(String bus, String scene, String useCase, Class extractClass) { + this.bus = bus; + this.scene = scene; + this.useCase = useCase; + this.extractClass = extractClass; + } + + + ExtractCoordinate(Extract extract, Class extractClass) { + this.bus = extract.bus(); + this.scene = extract.scene(); + this.useCase = extract.useCase(); + this.extractClass = extractClass; + } + + public static ExtractCoordinate build(String bus) { + return new ExtractCoordinate(bus, null, null, null); + } + + public static ExtractCoordinate build(String bus, String scene) { + return new ExtractCoordinate(bus, scene, null, null); + } + + public static ExtractCoordinate build(String bus, String scene, String useCase) { + return new ExtractCoordinate(bus, scene, useCase, null); + } + + public String getBus() { + return bus; + } + + public String getScene() { + return scene; + } + + public String getUseCase() { + return useCase; + } + + public Class getExtractClass() { + return extractClass; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ExtractCoordinate)) { + return false; + } + ExtractCoordinate that = (ExtractCoordinate) o; + if(!ObjectUtils.isEmpty(bus) && !ObjectUtils.isEmpty(scene) && !ObjectUtils.isEmpty(useCase)){ + return Objects.equals(getBus(), that.getBus()) && + Objects.equals(getScene(), that.getScene()) && + Objects.equals(getUseCase(), that.getUseCase()); + } + + if(!ObjectUtils.isEmpty(bus) && !ObjectUtils.isEmpty(scene)){ + return Objects.equals(getBus(), that.getBus()) && + Objects.equals(getScene(), that.getScene()); + } + + if(!ObjectUtils.isEmpty(bus)){ + return Objects.equals(getBus(), that.getBus()); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash(getBus(), getScene(), getUseCase()); + } + + @Override + public String toString() { + return "ExtractCoordinate{" + + "bus='" + bus + '\'' + + ", scene='" + scene + '\'' + + ", useCase='" + useCase + '\'' + + '}'; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java new file mode 100755 index 0000000..7090fa9 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/ExtractFactory.java @@ -0,0 +1,97 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.extract; + +import java.util.*; + +/** + * 扩展工厂 + * @author starBlues + * @version 3.0.0 + */ +public interface ExtractFactory { + + ExtractFactory INSTANT = new DefaultExtractFactory(); + + /** + * 获取实例 + * @return ExtractFactory + */ + static ExtractFactory getInstant(){ + return INSTANT; + } + + /** + * 通过坐标得到扩展 + * @param coordinate 扩展的坐标 + * @param 扩展的泛型 + * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 + */ + T getExtractByCoordinate(ExtractCoordinate coordinate); + + /** + * 根据插件id和坐标得到扩展 + * @param pluginId 插件id + * @param coordinate 扩展的坐标 + * @param 扩展的泛型 + * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 + */ + T getExtractByCoordinate(String pluginId, ExtractCoordinate coordinate); + + + /** + * 根据坐标得到主程序的扩展 + * 主程序扩展必须使用 @Extract+@Component 进行定义 + * @param coordinate 扩展的坐标 + * @param 扩展的泛型 + * @return 扩展实例, 如果不存在则抛出 RuntimeException 异常 + */ + T getExtractByCoordinateOfMain(ExtractCoordinate coordinate); + + /** + * 根据接口类型获取扩展 + * @param interfaceClass 接口类类型 + * @param 接口类型泛型 + * @return 扩展实现集合 + */ + List getExtractByInterClass(Class interfaceClass); + + /** + * 根据插件id和接口类型获取扩展 + * @param pluginId 插件id + * @param interfaceClass 接口类类型 + * @param 接口类型泛型 + * @return 扩展实现集合 + */ + List getExtractByInterClass(String pluginId, Class interfaceClass); + + /** + * 根据接口类型获取主程序的扩展 + * 主程序扩展必须使用 @Extract+@Component 进行定义 + * @param interfaceClass 接口类类型 + * @param 接口类型泛型 + * @return 扩展实现集合 + */ + List getExtractByInterClassOfMain(Class interfaceClass); + + /** + * 得到所有的扩展坐标 + * @return 扩展坐标集合, key 为插件id, 值为所有扩展坐标集合 + */ + Map> getExtractCoordinates(); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java new file mode 100755 index 0000000..1ac04fa --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/extract/OpExtractFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.extract; + +/** + * 可操作的扩展工厂 + * @author starBlues + * @version 3.0.0 + */ +public interface OpExtractFactory extends ExtractFactory{ + + /** + * 添加main中的扩展 + * @param extractObject extractObject + */ + void addOfMain(Object extractObject); + + /** + * 添加插件中的扩展 + * @param pluginId 插件 + * @param extractObject 扩展对象 + */ + void add(String pluginId, Object extractObject); + + /** + * 移除插件中的扩展 + * @param pluginId 插件id + */ + void remove(String pluginId); + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java new file mode 100755 index 0000000..e993667 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/DefaultInvokeSupperCache.java @@ -0,0 +1,81 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.invoke; + +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import com.gitee.starblues.utils.ObjectUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 默认的 InvokeSupperCache + * @author starBlues + * @version 3.0.0 + */ +public class DefaultInvokeSupperCache implements InvokeSupperCache{ + + private final Map> invokeSupplierCache = new ConcurrentHashMap<>(); + + @Override + public Object getSupperBean(String supperKey){ + return getSupperBean(null, supperKey); + } + + @Override + public Object getSupperBean(String pluginId, String supperKey){ + if(!ObjectUtils.isEmpty(pluginId)){ + Map cacheMap = invokeSupplierCache.get(pluginId); + if(cacheMap == null){ + return null; + } + return getSupperBean(cacheMap.get(supperKey)); + } + for (Map value : invokeSupplierCache.values()) { + Object supperBean = getSupperBean(value.get(supperKey)); + if(supperBean != null){ + return supperBean; + } + } + return null; + } + + @Override + public void add(String pluginId, SupperCache cache){ + Map supperCache = invokeSupplierCache.computeIfAbsent(pluginId, k -> new HashMap<>()); + supperCache.put(cache.getSupperKey(), cache); + } + + @Override + public void remove(String pluginId){ + invokeSupplierCache.remove(pluginId); + } + + private static Object getSupperBean(SupperCache cache){ + if(cache == null){ + return null; + } + ApplicationContext applicationContext = cache.getApplicationContext(); + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + if(springBeanFactory.containsBean(cache.getBeanName())){ + return springBeanFactory.getBean(cache.getBeanName()); + } + return null; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java new file mode 100755 index 0000000..5e8d923 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/InvokeSupperCache.java @@ -0,0 +1,57 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.invoke; + + +/** + * 插件调用提供者缓存 + * @author starBlues + * @version 3.0.0 + */ +public interface InvokeSupperCache { + + /** + * 获取提供者bean + * @param pluginId 插件id + * @param supperKey 提供者key + * @return Object + */ + Object getSupperBean(String pluginId, String supperKey); + + /** + * 获取提供者bean + * @param supperKey 提供者key + * @return Object + */ + Object getSupperBean(String supperKey); + + /** + * 添加提供者 + * @param pluginId 插件id + * @param cache 提供者缓存 + */ + void add(String pluginId, SupperCache cache); + + /** + * 移除插件提供者 + * @param pluginId 插件id + */ + void remove(String pluginId); + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java new file mode 100755 index 0000000..019aed0 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/invoke/SupperCache.java @@ -0,0 +1,50 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.invoke; + +import com.gitee.starblues.spring.ApplicationContext; + +/** + * 提供者缓存包装 + * @author starBlues + * @version 3.0.0 + */ +public class SupperCache { + + private final String supperKey; + private final String beanName; + private final ApplicationContext applicationContext; + + public SupperCache(String supperKey, String beanName, ApplicationContext applicationContext) { + this.supperKey = supperKey; + this.beanName = beanName; + this.applicationContext = applicationContext; + } + + public String getSupperKey() { + return supperKey; + } + + public String getBeanName() { + return beanName; + } + + public ApplicationContext getApplicationContext() { + return applicationContext; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java new file mode 100755 index 0000000..533137f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginResource.java @@ -0,0 +1,184 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.web; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NestedIOException; +import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; + +/** + * 插件资源实现类.主要是对Spring中的抽象的Resource实现. + * 功能: 主要是获取插件包中的文件资源。 + * @author starBlues + * @version 3.0.0 + */ +public class PluginResource implements Resource { + + private final static Logger log = LoggerFactory.getLogger(PluginResource.class); + + private ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); + private final PluginDescriptor pluginDescriptor; + + private final long lastModified; + private final String path; + + + /** + * 相对Classpath 路径 + * @param path 路径 + * @param pluginDescriptor pluginDescriptor + */ + public PluginResource(String path, PluginDescriptor pluginDescriptor) { + String pathToUse = StringUtils.cleanPath(path); + if (pathToUse.startsWith("/")) { + pathToUse = pathToUse.substring(1); + } + this.path = pathToUse; + this.pluginDescriptor = pluginDescriptor; + this.lastModified = System.currentTimeMillis(); + } + + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public InputStream getInputStream() throws IOException { + return classLoader.getResourceAsStream(path); + } + + + @Override + public long contentLength() throws IOException { + URL url = getURL(); + if (ResourceUtils.isFileURL(url)) { + return getFile().length(); + } + if(ResourceUtils.isJarURL(url)){ + URLConnection con = getURL().openConnection(); + return con.getContentLength(); + } + return 0L; + } + + @Override + public long lastModified() throws IOException { + return lastModified; + } + + + @Override + public Resource createRelative(String relativePath) { + throw new RuntimeException("This method is not supported"); + } + + @Override + public String getFilename() { + return StringUtils.getFilename(this.path); + } + + @Override + public String getDescription() { + return pluginDescriptor.getDescription(); + } + + @Override + public boolean exists() { + try { + URL url = getURL(); + if(url == null){ + return false; + } + if (ResourceUtils.isFileURL(url)) { + return getFile().exists(); + } + if (contentLength() >= 0) { + return true; + } + InputStream is = getInputStream(); + is.close(); + return true; + } catch (Exception e){ + log.debug(e.getMessage(), e); + return false; + } + } + + @Override + public boolean isReadable() { + try { + URL url = getURL(); + if (ResourceUtils.isFileURL(url)) { + File file = getFile(); + return (file.canRead() && !file.isDirectory()); + } else { + return true; + } + } catch (Exception e) { + log.debug(e.getMessage(), e); + return false; + } + } + + @Override + public boolean isOpen() { + return false; + } + + + + @Override + public File getFile() throws IOException { + URL url = getURL(); + if (ResourceUtils.isJarURL(url)) { + URL actualUrl = ResourceUtils.extractArchiveURL(url); + return ResourceUtils.getFile(actualUrl, "Jar URL"); + } else { + return ResourceUtils.getFile(url, getDescription()); + } + } + + @Override + public URL getURL() throws IOException { + return classLoader.getResource(path); + } + + @Override + public URI getURI() throws IOException { + URL url = getURL(); + try { + return ResourceUtils.toURI(url); + } catch (URISyntaxException ex) { + throw new NestedIOException("Invalid URI [" + url + "]", ex); + } + } + +} + diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java new file mode 100755 index 0000000..5be0e76 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceConfig.java @@ -0,0 +1,84 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.web; + +import com.gitee.starblues.utils.Assert; +import com.gitee.starblues.utils.UrlUtils; +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.CacheControl; + +/** + * 插件PluginStaticResourceConfig + * @author starBlues + * @version 3.0.0 + */ +public class PluginStaticResourceConfig { + + public static final String DEFAULT_PLUGIN_STATIC_RESOURCE_PATH_PREFIX = "static-plugin"; + public static final String DEFAULT_INDEX_PAGE_NAME = "index.html"; + private static final Logger log = LoggerFactory.getLogger(PluginStaticResourceConfig.class); + + /** + * 插件静态资源访问前缀 + */ + private String pathPrefix = DEFAULT_PLUGIN_STATIC_RESOURCE_PATH_PREFIX; + + /** + * 默认首页页面名称 + */ + private String indexPageName = DEFAULT_INDEX_PAGE_NAME; + + /** + * 页面是否缓存 + */ + private CacheControl cacheControl = CacheControl.noCache(); + + + public void logPathPrefix(){ + log.info("插件静态资源访问前缀配置为: /{}/{pluginId}", pathPrefix); + } + + + public String getPathPrefix() { + return pathPrefix; + } + + public void setPathPrefix(String pathPrefix) { + Assert.isNotEmpty(pathPrefix, "配置 pathPrefix 不能为空"); + this.pathPrefix = UrlUtils.format(pathPrefix); + } + + public String getIndexPageName() { + return indexPageName; + } + + public void setIndexPageName(String indexPageName) { + Assert.isNotEmpty(pathPrefix, "配置 indexPageName 不能为空"); + this.indexPageName = indexPageName; + } + + public CacheControl getCacheControl() { + return cacheControl; + } + + public void setCacheControl(CacheControl cacheControl) { + Assert.isNotNull(pathPrefix, "配置 cacheControl 不能为空"); + this.cacheControl = cacheControl; + } +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java new file mode 100755 index 0000000..2d4243b --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceResolver.java @@ -0,0 +1,407 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.web; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import com.gitee.starblues.spring.WebConfig; +import com.gitee.starblues.utils.MsgUtils; +import com.gitee.starblues.utils.ObjectUtils; +import com.gitee.starblues.utils.UrlUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.FileUrlResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.resource.AbstractResourceResolver; +import org.springframework.web.servlet.resource.ResourceResolverChain; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 插件web静态资源Resolver + * @author starBlues + * @version 3.0.2 + */ +public class PluginStaticResourceResolver extends AbstractResourceResolver { + + private final static Logger logger = LoggerFactory.getLogger(PluginStaticResourceResolver.class); + + private final static String RESOLVED_RESOURCE_CACHE_KEY_PREFIX = "resolvedPluginResource:"; + + private final static Map PLUGIN_RESOURCE_MAP = new ConcurrentHashMap<>(); + + private final PluginStaticResourceConfig config; + + public PluginStaticResourceResolver(PluginStaticResourceConfig config) { + this.config = config; + } + + + @Override + protected Resource resolveResourceInternal(HttpServletRequest request, + String requestPath, List locations, + ResourceResolverChain chain) { + if(request != null){ + String requestUri = request.getRequestURI(); + String formatUri = UrlUtils.format(requestUri); + // fix https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I53T9W + requestPath = UrlUtils.format(formatUri.replaceFirst(config.getPathPrefix(), "")); + } + int startOffset = requestPath.indexOf("/"); + String pluginId = null; + String partialPath = null; + if (startOffset == -1) { + pluginId = requestPath; + partialPath = config.getIndexPageName(); + } else { + pluginId = requestPath.substring(0, startOffset); + partialPath = requestPath.substring(startOffset + 1); + } + + PluginStaticResource pluginResource = PLUGIN_RESOURCE_MAP.get(pluginId); + + if(pluginResource == null){ + return chain.resolveResource(request, requestPath, locations); + } + + String key = computeKey(request, requestPath); + // 先判断缓存中是否存在。 + Resource resource = pluginResource.getCacheResource(key); + if(resource != null){ + return resource; + } + resource = findResource(pluginResource, partialPath); + if(resource != null){ + pluginResource.putCacheResource(key, resource); + return resource; + } else { + // 尝试获取首页页面 + String indexPageName = config.getIndexPageName(); + if(ObjectUtils.isEmpty(indexPageName)){ + indexPageName = PluginStaticResourceConfig.DEFAULT_INDEX_PAGE_NAME; + } + if(partialPath.lastIndexOf(".") > -1){ + // 存在后缀 + return null; + } + + // 查找第一级节点,找不到则读取根index.html + if(partialPath.contains(UrlUtils.PATH_SEPARATOR)){ + partialPath = partialPath.substring(0, partialPath.indexOf(UrlUtils.PATH_SEPARATOR)); + } + // 第一级节点 + resource = findResource(pluginResource, UrlUtils.joiningUrlPath(partialPath, indexPageName)); + if(resource != null){ + return resource; + } + // 根节点 + return findResource(pluginResource, UrlUtils.joiningUrlPath(UrlUtils.PATH_SEPARATOR, indexPageName)); + } + } + + private Resource findResource(PluginStaticResource pluginResource, String partialPath){ + // 从classpath 获取资源 + Resource resource = resolveClassPath(pluginResource, partialPath); + if(resource != null){ + return resource; + } + + // 从外置文件路径获取资源 + resource = resolveFilePath(pluginResource, partialPath); + if(resource != null){ + return resource; + } + return resource; + } + + /** + * 解决 ClassPath 的资源文件。也就是插件中定义的 classpath:/xx/xx/ 配置 + * @param pluginResource 插件资源配置Bean + * @param partialPath 部分路径 + * @return 资源。没有发现则返回null + */ + private Resource resolveClassPath(PluginStaticResource pluginResource, String partialPath){ + Set classPaths = pluginResource.getClassPaths(); + if(classPaths == null || classPaths.isEmpty()){ + return null; + } + + ClassLoader pluginClassLoader = pluginResource.getPluginClassLoader(); + for (String classPath : classPaths) { + try { + PluginResource resource = new PluginResource(classPath + partialPath, pluginResource.getPluginDescriptor()); + resource.setClassLoader(pluginClassLoader); + if(resource.exists()){ + // 确保资源为文件 + File file = resource.getFile(); + if(file != null && file.isFile()){ + return resource; + } + } + } catch (Exception e){ + logger.debug("Get static resources of classpath '{}' error.", classPath, e); + } + } + return null; + } + + /** + * 解决插件中配置的绝对文件路径的文件资源。也就是插件中定义的 file:D://xx/xx/ 配置 + * @param pluginResource 插件资源配置Bean + * @param partialPath 部分路径 + * @return 资源。没有发现则返回null + */ + private Resource resolveFilePath(PluginStaticResource pluginResource, String partialPath) { + Set filePaths = pluginResource.getFilePaths(); + if(filePaths == null || filePaths.isEmpty()){ + return null; + } + + for (String filePath : filePaths) { + Path fullPath = Paths.get(filePath + partialPath); + if(!Files.exists(fullPath)){ + continue; + } + try { + FileUrlResource fileUrlResource = new FileUrlResource(fullPath.toString()); + if(fileUrlResource.exists()){ + return fileUrlResource; + } + } catch (Exception e) { + logger.debug("Get static resources of path '{}' error.", fullPath, e); + } + } + return null; + } + + + @Override + protected String resolveUrlPathInternal(String resourceUrlPath, + List locations, + ResourceResolverChain chain) { + return null; + } + + /** + * 计算key + * @param request request + * @param requestPath 请求路径 + * @return 返回key + */ + protected String computeKey(HttpServletRequest request, String requestPath) { + StringBuilder key = new StringBuilder(RESOLVED_RESOURCE_CACHE_KEY_PREFIX); + key.append(requestPath); + if (request != null) { + String codingKey = getContentCodingKey(request); + if (ObjectUtils.hasText(codingKey)) { + key.append("+encoding=").append(codingKey); + } + } + return key.toString(); + } + + /** + * 根据请求获取内容code key + * @param request request + * @return key + */ + private String getContentCodingKey(HttpServletRequest request) { + String header = request.getHeader(HttpHeaders.ACCEPT_ENCODING); + if (!ObjectUtils.hasText(header)) { + return null; + } + return Arrays.stream(StringUtils.tokenizeToStringArray(header, ",")) + .map(token -> { + int index = token.indexOf(';'); + return (index >= 0 ? token.substring(0, index) : token).trim().toLowerCase(); + }) + .sorted() + .collect(Collectors.joining(",")); + } + + + + /** + * 每新增一个插件, 都需要调用该方法,来解析该插件的 StaticResourceConfig 配置。并将其保存到 StaticResourceConfig bean 中。 + * @param pluginDescriptor 插件信息 + * @param pluginClassLoader 插件classloader + * @param webConfig web配置 + */ + public static synchronized void parse(PluginDescriptor pluginDescriptor, + ClassLoader pluginClassLoader, + WebConfig webConfig){ + if(webConfig == null || !webConfig.isEnable()){ + return; + } + final Set locations = webConfig.getResourceLocations(); + if(ObjectUtils.isEmpty(locations)){ + return; + } + + Set classPaths = new HashSet<>(); + Set filePaths = new HashSet<>(); + + String pluginId = pluginDescriptor.getPluginId(); + + for (String location : locations) { + if(ObjectUtils.isEmpty(location)){ + continue; + } + final int first = location.indexOf(":"); + if(first == -1){ + logger.warn("插件[{}]配置的静态资源格式错误: {}", + MsgUtils.getPluginUnique(pluginDescriptor), location); + continue; + } + String type = location.substring(0, first); + String path = location.substring(first+1); + + if("classpath".equalsIgnoreCase(type)){ + if(path.startsWith("/")){ + path = path.substring(1); + } + if(!path.endsWith("/")){ + path = path + "/"; + } + classPaths.add(path); + } else if("file".equalsIgnoreCase(type)){ + if(!path.endsWith(File.separator)){ + path = path + File.separator; + } + filePaths.add(path); + } else { + logger.warn("插件[{}]配置的静态资源类型不能识别: {}", MsgUtils.getPluginUnique(pluginDescriptor), type); + } + } + + PluginStaticResource pluginResource = new PluginStaticResource(); + pluginResource.setClassPaths(classPaths); + pluginResource.setFilePaths(filePaths); + pluginResource.setPluginDescriptor(pluginDescriptor); + pluginResource.setPluginClassLoader(pluginClassLoader); + + logger.info("插件[{}]配置的静态资源: classpath[{}], file[{}]", MsgUtils.getPluginUnique(pluginDescriptor), + classPaths, filePaths); + + if(PLUGIN_RESOURCE_MAP.containsKey(pluginId)){ + // 如果存在该插件id的插件资源信息, 则先移除它 + remove(pluginId); + } + PLUGIN_RESOURCE_MAP.put(pluginId, pluginResource); + } + + + + /** + * 卸载插件时。调用该方法移除插件的资源信息 + * @param pluginId 插件id + */ + public static synchronized void remove(String pluginId){ + PluginStaticResource pluginResource = PLUGIN_RESOURCE_MAP.get(pluginId); + if(pluginResource == null){ + return; + } + PLUGIN_RESOURCE_MAP.remove(pluginId); + } + + /** + * 插件资源解析后的信息 + */ + private static class PluginStaticResource { + + /** + * basePlugin bean + */ + private PluginDescriptor pluginDescriptor; + + /** + * 插件classloader + */ + private ClassLoader pluginClassLoader; + + /** + * 定义的classpath集合 + */ + private Set classPaths; + + /** + * 定义的文件路径集合 + */ + private Set filePaths; + + /** + * 缓存的资源。key 为资源的可以。值为资源 + */ + private final Map cacheResourceMaps = new ConcurrentHashMap<>(); + + PluginDescriptor getPluginDescriptor() { + return pluginDescriptor; + } + + void setPluginDescriptor(PluginDescriptor pluginDescriptor) { + this.pluginDescriptor = pluginDescriptor; + } + + ClassLoader getPluginClassLoader() { + return pluginClassLoader; + } + + void setPluginClassLoader(ClassLoader pluginClassLoader) { + this.pluginClassLoader = pluginClassLoader; + } + + Set getClassPaths() { + return classPaths; + } + + void setClassPaths(Set classPaths) { + this.classPaths = classPaths; + } + + Set getFilePaths() { + return filePaths; + } + + void setFilePaths(Set filePaths) { + this.filePaths = filePaths; + } + + + Resource getCacheResource(String key){ + return cacheResourceMaps.get(key); + } + + void putCacheResource(String key, Resource resource){ + if(StringUtils.isEmpty(key) || resource == null){ + return; + } + cacheResourceMaps.put(key, resource); + } + } + +} + + + diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java new file mode 100755 index 0000000..c04dc7f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/spring/web/PluginStaticResourceWebMvcConfigurer.java @@ -0,0 +1,53 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.spring.web; + +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 插件静态资源webMvc配置 + * @author starBlues + * @version 3.0.0 + */ +public class PluginStaticResourceWebMvcConfigurer implements WebMvcConfigurer { + + private final PluginStaticResourceConfig resourceConfig; + + public PluginStaticResourceWebMvcConfigurer(PluginStaticResourceConfig resourceConfig) { + this.resourceConfig = resourceConfig; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + String pathPattern = "/" + resourceConfig.getPathPrefix() + "/**"; + ResourceHandlerRegistration resourceHandlerRegistration = registry.addResourceHandler(pathPattern); + CacheControl cacheControl = resourceConfig.getCacheControl(); + if(cacheControl != null){ + resourceHandlerRegistration.setCacheControl(cacheControl); + } else { + resourceHandlerRegistration.setCacheControl(CacheControl.noStore()); + } + resourceHandlerRegistration + .resourceChain(false) + .addResolver(new PluginStaticResourceResolver(resourceConfig)); + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java new file mode 100755 index 0000000..def02b2 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/AnnotationsUtils.java @@ -0,0 +1,70 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import java.lang.annotation.Annotation; + +/** + * 注解工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class AnnotationsUtils { + + private AnnotationsUtils(){ + + } + + + /** + * 存在注解判断 + * @param aClass 类 + * @param isAllMatch 是否匹配全部注解 + * @param annotationClasses 注解类 + * @return boolean + */ + @SafeVarargs + public static boolean haveAnnotations(Class aClass, boolean isAllMatch, + Class ...annotationClasses){ + if(aClass == null){ + return false; + } + if(annotationClasses == null){ + return false; + } + for (Class annotationClass : annotationClasses) { + Annotation annotation = aClass.getAnnotation(annotationClass); + if(isAllMatch){ + if(annotation == null){ + return false; + } + } else { + if(annotation != null){ + return true; + } + } + } + if(isAllMatch){ + return true; + } else { + return false; + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java new file mode 100755 index 0000000..3cb3189 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ClassUtils.java @@ -0,0 +1,141 @@ +/** + * Copyright [2019-Present] [starBlues] + *

+ * Licensed 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 com.gitee.starblues.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 类工具类 + * + * @author starBlues + * @since 2.4.0 + * @version 2.4.0 + */ +public class ClassUtils { + + private ClassUtils() { + + } + + public static List getAllFields(Class clazz) { + List fieldList = new ArrayList<>(); + while (clazz != null) { + fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); + clazz = clazz.getSuperclass(); + } + return fieldList; + } + + /** + * 通过反射获取字段 + * + * @param o 对象 + * @param fieldName 字段名称 + * @param 字段类型 + * @return 字段值 + * @throws IllegalAccessException 异常信息 + */ + public static T getReflectionField(Object o, String fieldName) throws IllegalAccessException { + if (o == null) { + return null; + } + + Field templateResolversField = ReflectionUtils.findField(o.getClass(), + fieldName); + return getReflectionField(templateResolversField, o); + } + + /** + * 通过反射获取字段 + * + * @param o 对象 + * @param fieldName 字段名称 + * @param fieldClassType 字段类型 + * @param 字段类型 + * @return 字段值 + * @throws IllegalAccessException 异常信息 + */ + public static T getReflectionField(Object o, String fieldName, Class fieldClassType) throws IllegalAccessException { + if (o == null) { + return null; + } + Field templateResolversField = ReflectionUtils.findField(o.getClass(), + fieldName, fieldClassType); + return getReflectionField(templateResolversField, o); + } + + /** + * 通过反射Field获取字段 + * + * @param field Field字段 + * @param o 当前对象 + * @param 字段类型 + * @return 字段值 + * @throws IllegalAccessException 异常信息 + */ + @SuppressWarnings("unchecked") + public static T getReflectionField(Field field, Object o) throws IllegalAccessException { + if (field == null) { + return null; + } + field.setAccessible(true); + Object fieldObject = field.get(o); + return (T) fieldObject; + } + + /** + * 得到注解修改者 + * + * TODO 可能某个java版本不生效 + * @param annotation 注解 + * @return 修改者集合 + * @throws Exception 异常 + */ + @SuppressWarnings("unchecked") + public static Map getAnnotationsUpdater(Object annotation) throws Exception { + InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation); + Field field = getAnnotationsUpdaterField(invocationHandler); + if (field == null) { + return null; + } + return (Map) field.get(invocationHandler); + } + + private static Field getAnnotationsUpdaterField(InvocationHandler invocationHandler) { + Class aClass = invocationHandler.getClass(); + Field field = ReflectionUtils.findField(aClass, "memberValues", Map.class); + if (field == null) { + field = ReflectionUtils.findField(aClass, "valueCache", Map.class); + } + if (field == null) { + field = ReflectionUtils.findField(aClass, Map.class); + } + if (field != null) { + field.setAccessible(true); + return field; + } else { + return null; + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java new file mode 100755 index 0000000..9b90e8a --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/LogUtils.java @@ -0,0 +1,41 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import com.gitee.starblues.core.descriptor.PluginDescriptor; +import org.slf4j.Logger; + +/** + * 日志打印工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public class LogUtils { + + private LogUtils(){} + + public static void info(Logger logger, PluginDescriptor pluginDescriptor, String msg){ + logger.info("插件[{}]{}", MsgUtils.getPluginUnique(pluginDescriptor), msg); + } + + public static String getMsg(PluginDescriptor pluginDescriptor, String msg, Object... args){ + return "插件[ " + MsgUtils.getPluginUnique(pluginDescriptor) + " ]" + String.format(msg, args); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java new file mode 100755 index 0000000..16628b2 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/MsgUtils.java @@ -0,0 +1,52 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import com.gitee.starblues.core.descriptor.PluginDescriptor; + +/** + * msg 工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public abstract class MsgUtils { + + private MsgUtils(){} + + public static String getPluginUnique(PluginDescriptor pluginDescriptor){ + return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getPluginVersion(); + } + + public static String getPluginUnique(String pluginId, String version){ + if(ObjectUtils.isEmpty(version)){ + return pluginId; + } + return pluginId + "@" + version; + } + + public static String getThrowableMsg(Throwable throwable){ + return ObjectUtils.isEmpty(throwable.getMessage()) ? "" : throwable.getMessage(); + } + + public static String getThrowableMsg(String message){ + return ObjectUtils.isEmpty(message) ? "" : message; + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java new file mode 100755 index 0000000..8f3e076 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/Order.java @@ -0,0 +1,34 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +/** + * 排序接口 + * @author starBlues + * @since 1.0.0 + * @version 1.0.0 + */ +public interface Order { + + /** + * 排序, 数字越大越先执行 + * @return OrderPriority + */ + OrderPriority order(); + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java new file mode 100755 index 0000000..bf4126f --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderPriority.java @@ -0,0 +1,119 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +/** + * 顺序优先级 + * + * @author starBlues + * @since 1.0.0 + * @version 1.0.0 + */ +public class OrderPriority { + /** + * 高优先级 + */ + private static final Integer HIGH_PRIORITY = 1000; + + /** + * 中优先级 + */ + private static final Integer MIDDLE_PRIORITY = 0; + + /** + * 低优先级 + */ + private static final Integer LOW_PRIORITY = -1000; + + + private Integer priority; + + + private OrderPriority(Integer priority){ + this.priority = priority; + } + + public Integer getPriority() { + return priority; + } + + /** + * 得到低优先级 + * @return OrderPriority + */ + public static OrderPriority getLowPriority(){ + return new OrderPriority(LOW_PRIORITY); + } + + + /** + * 得到中优先级 + * @return OrderPriority + */ + public static OrderPriority getMiddlePriority(){ + return new OrderPriority(MIDDLE_PRIORITY); + } + + + /** + * 得到高优先级 + * @return OrderPriority + */ + public static OrderPriority getHighPriority(){ + return new OrderPriority(HIGH_PRIORITY); + } + + + /** + * 升优先级.最高只能升到最高优先级别。也就是1000 + * @param number 升级数量 + * @return OrderPriority + */ + public OrderPriority up(Integer number){ + if(number == null){ + return this; + } + if(HIGH_PRIORITY.equals(this.priority) || (this.priority + number) > HIGH_PRIORITY){ + this.priority = HIGH_PRIORITY; + return this; + } else { + this.priority = this.priority + number; + return this; + } + } + + /** + * 降优先级.最低只能降到最低优先级别。也就是-1000 + * @param number 降级数量 + * @return OrderPriority + */ + public OrderPriority down(Integer number){ + if(number == null){ + return this; + } + if(MIDDLE_PRIORITY.equals(this.priority) || (this.priority - number) < MIDDLE_PRIORITY){ + this.priority = MIDDLE_PRIORITY; + return this; + } else { + this.priority = this.priority - number; + return this; + } + } + + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java new file mode 100755 index 0000000..89720e8 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/OrderUtils.java @@ -0,0 +1,68 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; + +/** + * 通用工具 + * + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class OrderUtils { + + private OrderUtils(){} + + /** + * list按照int排序. 数字越大, 越排在前面 + * @param list list集合 + * @param orderImpl 排序实现 + * @param T + * @return List + */ + public static List order(List list, Function orderImpl){ + if(list == null){ + return null; + } + list.sort(Comparator.comparing(orderImpl, Comparator.nullsLast(Comparator.reverseOrder()))); + return list; + } + + + /** + * 对 OrderPriority 进行排序操作 + * @param order OrderPriority + * @param 当前操作要被排序的bean + * @return Comparator + */ + public static Comparator orderPriority(final Function order){ + return Comparator.comparing(t -> { + OrderPriority orderPriority = order.apply(t); + if(orderPriority == null){ + orderPriority = OrderPriority.getLowPriority(); + } + return orderPriority.getPriority(); + }, Comparator.nullsLast(Comparator.reverseOrder())); + } + + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java new file mode 100755 index 0000000..d6d8657 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginConfigUtils.java @@ -0,0 +1,134 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import com.gitee.starblues.core.RuntimeMode; +import com.gitee.starblues.integration.IntegrationConfiguration; + +/** + * 插件配置工具类 + * @author starBlues + * @since 3.0.0 + * @version 3.0.0 + */ +public class PluginConfigUtils { + + private static final String DO = "-"; + + private PluginConfigUtils(){} + + /** + * 根据项目运行环境模式来获取配置文件名称 + * @param fileName 文件名称 + * @param prodSuffix 生产环境前缀 + * @param devSuffix 开发环境前缀 + * @param runtimeMode 运行模式 + * @return 文件名称 + */ + public static FileNamePack getConfigFileName(String fileName, + String prodSuffix, + String devSuffix, + RuntimeMode runtimeMode){ + if(ObjectUtils.isEmpty(fileName)){ + return null; + } + String suffix = ""; + if(runtimeMode == RuntimeMode.PROD){ + // 生产环境 + suffix = prodSuffix; + } else if(runtimeMode == RuntimeMode.DEV){ + // 开发环境 + suffix = devSuffix; + } + + return new FileNamePack(fileName, suffix); + } + + public static String joinConfigFileName(FileNamePack fileNamePack){ + if(fileNamePack == null){ + return null; + } + return joinConfigFileName(fileNamePack.getSourceFileName(), fileNamePack.getFileSuffix()); + } + + public static String joinConfigFileName(String fileName, String suffix){ + if(ObjectUtils.isEmpty(fileName)){ + return null; + } + String fileNamePrefix; + String fileNamePrefixSuffix; + + if(fileName.lastIndexOf(".") == -1) { + fileNamePrefix = fileName; + fileNamePrefixSuffix = ""; + } else { + int index = fileName.lastIndexOf("."); + fileNamePrefix = fileName.substring(0, index); + fileNamePrefixSuffix = fileName.substring(index); + } + if(suffix == null){ + suffix = ""; + } + if(ObjectUtils.isEmpty(suffix) && !suffix.startsWith(DO)){ + suffix = DO + suffix; + } + return fileNamePrefix + suffix + fileNamePrefixSuffix; + } + + /** + * 得到插件接口前缀 + * @param configuration 配置 + * @param pluginId 插件id + * @return 接口前缀 + */ + public static String getPluginRestPrefix(IntegrationConfiguration configuration, String pluginId){ + String pathPrefix = configuration.pluginRestPathPrefix(); + if(configuration.enablePluginIdRestPathPrefix()){ + if(pathPrefix != null && !"".equals(pathPrefix)){ + pathPrefix = UrlUtils.restJoiningPath(pathPrefix, pluginId); + } else { + pathPrefix = pluginId; + } + return pathPrefix; + } else { + if(pathPrefix == null || "".equals(pathPrefix)){ + // 不启用插件id作为路径前缀, 并且路径前缀为空, 则直接返回。 + return null; + } + } + return pathPrefix; + } + + public static class FileNamePack { + private final String sourceFileName; + private final String fileSuffix; + + public FileNamePack(String sourceFileName, String fileSuffix) { + this.sourceFileName = sourceFileName; + this.fileSuffix = fileSuffix; + } + + public String getSourceFileName() { + return sourceFileName; + } + + public String getFileSuffix() { + return fileSuffix; + } + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java new file mode 100755 index 0000000..acbd9a0 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/PluginFileUtils.java @@ -0,0 +1,275 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import com.gitee.starblues.common.ManifestKey; +import com.gitee.starblues.common.PackageStructure; +import com.gitee.starblues.common.PackageType; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.*; +import java.math.BigInteger; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.Enumeration; +import java.util.List; +import java.util.Objects; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * 插件文件工具类 + * + * @author starBlues + * @since 3.0.0 + * @version 3.1.2 + */ +public final class PluginFileUtils { + + private static final String FILE_POINT = "."; + + private PluginFileUtils(){} + + + public static String getMd5ByFile(File file) throws FileNotFoundException { + String value = null; + FileInputStream in = new FileInputStream(file); + try { + MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()); + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(byteBuffer); + BigInteger bi = new BigInteger(1, md5.digest()); + value = bi.toString(16); + } catch (Exception e) { + e.printStackTrace(); + } finally { + IOUtils.closeQuietly(in); + } + return value; + } + + + public static void cleanEmptyFile(List paths){ + if(ObjectUtils.isEmpty(paths)){ + return; + } + for (String pathStr : paths) { + Path path = Paths.get(pathStr); + if(!Files.exists(path)){ + continue; + } + try { + Files.list(path) + .forEach(subPath -> { + File file = subPath.toFile(); + if(!file.isFile()){ + return; + } + long length = file.length(); + if(length == 0){ + try { + Files.deleteIfExists(subPath); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + /** + * 如果文件不存在, 则会创建 + * @param path 插件路径 + * @return 插件路径 + * @throws IOException 没有发现文件异常 + */ + public static File createExistFile(Path path) throws IOException { + Path parent = path.getParent(); + if(!Files.exists(parent)){ + Files.createDirectories(parent); + } + if(!Files.exists(path)){ + Files.createFile(path); + } + return path.toFile(); + } + + /** + * 得到文件名称 + * @param file 原始文件 + * @param includeSuffix 是否包含后缀 + * @return String + */ + public static String getFileName(File file, boolean includeSuffix){ + if(file == null){ + return null; + } + return getFileName(file.getName(), includeSuffix); + } + + + /** + * 获取不包含文件后缀的文件名称 + * @param fileName 文件名称 + * @return String + */ + public static String getFileName(String fileName){ + return getFileName(fileName, false); + } + + /** + * 得到文件名称 + * @param fileName 原始文件名称. 比如: file.txt , 则返回 file + * @param includeSuffix 是否包含后缀 + * @return String + */ + public static String getFileName(String fileName, boolean includeSuffix){ + if(ObjectUtils.isEmpty(fileName)){ + return fileName; + } + if(includeSuffix){ + return fileName; + } + int i = fileName.lastIndexOf(FILE_POINT); + if(i > 0){ + return fileName.substring(0, i); + } else { + return fileName; + } + } + + /** + * 创建随机文件 + * @param parentPath 父目录 + * @param isFile 是否为文件,不为文件就创建目录 + * @param fileSuffix 文件后缀 + * @return 创建的File + * @throws Exception 创建异常 + */ + public static File createRandomFile(String parentPath, boolean isFile, String fileSuffix) throws Exception{ + if(isFile){ + if(ObjectUtils.isEmpty(fileSuffix)){ + fileSuffix = ""; + } else { + if(!fileSuffix.startsWith(".")){ + fileSuffix = "." + fileSuffix; + } + } + File file = new File(parentPath, String.valueOf(System.currentTimeMillis()) + fileSuffix); + if(file.createNewFile()){ + return file; + } else { + throw new IOException("Cannot create file '" + file + "'."); + } + } else { + File file = new File(parentPath, String.valueOf(System.currentTimeMillis())); + FileUtils.forceMkdir(file); + return file; + } + } + + /** + * 判断是否能解压 + * @param zipPath zip路径 + * @return boolean + */ + public static boolean canDecompressZip(String zipPath){ + return ResourceUtils.isZip(zipPath) || ResourceUtils.isJar(zipPath); + } + + /** + * 解压zip 文件 + * @param zipPath zip 文件 + * @param targetDir 目标目录 + * @return 解压后的目录 + * @throws IOException 解压异常 + */ + public static File decompressZip(String zipPath, String targetDir) throws IOException { + File zipFile = new File(zipPath); + if(!canDecompressZip(zipPath)){ + throw new IOException("文件[" + zipFile.getName() + "]非压缩包, 不能解压"); + } + File targetDirFile = new File(targetDir, getFileName(zipFile, false)); + if(!targetDirFile.exists()){ + if(!targetDirFile.mkdirs()){ + throw new IOException("创建目录异常: " + targetDir); + } + targetDir = targetDirFile.getAbsolutePath(); + } else { + // 存在相同目录 + targetDirFile = new File(targetDir, getFileName(zipFile, false) + + "_" + System.currentTimeMillis()); + FileUtils.forceMkdir(targetDirFile); + targetDir = targetDirFile.getAbsolutePath(); + } + try (ZipFile zip = new ZipFile(zipFile, Charset.forName(PackageStructure.CHARSET_NAME))) { + Enumeration zipEnumeration = zip.entries(); + ZipEntry zipEntry = null; + while (zipEnumeration.hasMoreElements()) { + zipEntry = zipEnumeration.nextElement(); + String zipEntryName = zipEntry.getName(); + String currentZipPath = PackageStructure.resolvePath(zipEntryName); + String currentTargetPath = FilesUtils.joiningFilePath(targetDir, currentZipPath); + //判断路径是否存在,不存在则创建文件路径 + if (zipEntry.isDirectory()) { + File file = new File(currentTargetPath); + FileUtils.forceMkdir(file); + continue; + } else { + FilesUtils.createFile(currentTargetPath); + } + try (InputStream in = zip.getInputStream(zipEntry); + FileOutputStream out = new FileOutputStream(currentTargetPath)){ + if(PackageStructure.PROD_MANIFEST_PATH.equals(zipEntryName)){ + // 如果为 Manifest 文件, 则将打包类型切换为 xx-outer + resolveDecompressPluginType(in, out); + } else { + IOUtils.copy(in, out); + } + } + } + } + return targetDirFile; + } + + private static void resolveDecompressPluginType(InputStream inputStream, OutputStream outputStream) throws IOException{ + Manifest manifest = new Manifest(inputStream); + Attributes mainAttributes = manifest.getMainAttributes(); + String value = mainAttributes.getValue(ManifestKey.PLUGIN_PACKAGE_TYPE); + if(Objects.equals(value, PackageType.MAIN_PACKAGE_TYPE_JAR)){ + value = PackageType.PLUGIN_PACKAGE_TYPE_DIR; + } else if(Objects.equals(value, PackageType.PLUGIN_PACKAGE_TYPE_ZIP)){ + value = PackageType.PLUGIN_PACKAGE_TYPE_DIR; + } + mainAttributes.putValue(ManifestKey.PLUGIN_PACKAGE_TYPE, value); + manifest.write(outputStream); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java new file mode 100755 index 0000000..0fb3bd1 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/ScanUtils.java @@ -0,0 +1,150 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.util.ClassUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 扫描工具类 + * + * @author starBlues + * @version 2.2.2 + * @version 2.2.2 + */ +public class ScanUtils { + + /** + * 扫描指定包中的类。包括子包中的类 + * + * @param basePackage 包名 + * @param baseClass 当前操作的基础类 + * @return 类全路径(形如:xx.bb.cc) + * @throws IOException 扫描异常 + */ + public static Set scanClassPackageName(String basePackage, Class baseClass) throws IOException { + String osName = System.getProperty("os.name"); + if (osName.startsWith("Windows")) { + // windows + return scanClassPackageNameOfWindows(basePackage, baseClass); + } else { + // unix or linux + return scanClassPackageNameOfOther(basePackage, baseClass); + } + } + + /** + * 得到扫描的包 + * @param pluginClass 插件入口class + * @return 包集合 + */ + public static String[] getScanBasePackages(Class pluginClass){ + SpringBootApplication springBootApplication = pluginClass.getAnnotation(SpringBootApplication.class); + if(springBootApplication != null){ + String[] scanBasePackages = springBootApplication.scanBasePackages(); + if(scanBasePackages.length > 0){ + return scanBasePackages; + } + } + return new String[]{ pluginClass.getPackage().getName() }; + } + + /** + * 扫描windows环境下的类。包括子包中的类 + * @param basePackage 包名 + * @param baseClass 当前操作的基础类 + * @return 类全路径(形如:xx.bb.cc) + * @throws IOException 扫描异常 + */ + private static Set scanClassPackageNameOfWindows(String basePackage, Class baseClass) throws IOException { + String classpathRootPath = baseClass.getResource("/").getPath(); + final String classpath = classpathRootPath + .replace("/","\\") + .replaceFirst("\\\\",""); + // 把包名 packageName 转换为路径名 + basePackage = basePackage.replace(".", File.separator); + // class 文件全路径 + String fullPath = classpath + basePackage; + + return filterPath(fullPath).map(path -> { + String pathString = path.toString(); + return pathString + .replace(classpath, "") + .replace("\\",".") + .replace(".class",""); + }).collect(Collectors.toSet()); + } + + + /** + * 扫描linux/unix/mac环境下的类。包括子包中的类 + * @param basePackage 包名 + * @param baseClass 当前操作的基础类 + * @return 类全路径(形如:xx.bb.cc) + * @throws IOException 扫描异常 + */ + private static Set scanClassPackageNameOfOther(String basePackage, Class baseClass) throws IOException { + final String classpath = baseClass.getResource("/").getPath(); + // class 文件全路径 + String fullPath = classpath + ClassUtils.classPackageAsResourcePath(baseClass); + + return filterPath(fullPath).map(path -> { + String pathString = path.toString(); + // 去头去尾 + pathString = pathString + .replace(classpath, "") + .replace(".class", ""); + // 转换为 aa.bb.cc + return ClassUtils.convertResourcePathToClassName(pathString); + }).collect(Collectors.toSet()); + } + + /** + * 过滤类 + * @param fullPath 类的全路径 + * @return Stream + * @throws IOException IOException + */ + private static Stream filterPath(String fullPath) throws IOException { + return Files.walk(Paths.get(fullPath)) + .filter(Objects::nonNull) + .filter(Files::isRegularFile) + .filter(path -> { + String fileName = path.getFileName().toString(); + if(fileName == null){ + return false; + } + return fileName.endsWith(".class"); + }); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java new file mode 100755 index 0000000..331f4e7 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanCustomUtils.java @@ -0,0 +1,135 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import com.gitee.starblues.spring.ApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; + +import java.lang.annotation.Annotation; +import java.util.*; + +/** + * 自定义插件bean工具类 + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class SpringBeanCustomUtils { + + /** + * 获取bean名称 + * @param applicationContext ApplicationContext + * @return bean名称集合 + */ + public static Set getBeanName(ApplicationContext applicationContext){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + String[] beanDefinitionNames = springBeanFactory.getBeanDefinitionNames(); + Set set = new HashSet<>(beanDefinitionNames.length); + set.addAll(Arrays.asList(beanDefinitionNames)); + return set; + } + + /** + * 得到ApplicationContext中的bean的实现 + * @param applicationContext applicationContext + * @param aClass 接口或者抽象类型bean类型 + * @param 接口或者抽象类型bean类型 + * @return 所有的实现对象 + */ + public static List getBeans(ApplicationContext applicationContext, Class aClass) { + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + Map beansOfTypeMap = springBeanFactory.getBeansOfType(aClass); + if(beansOfTypeMap.isEmpty()){ + return new ArrayList<>(); + } + return new ArrayList<>(beansOfTypeMap.values()); + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext applicationContext + * @param aClass bean 类型 + * @param bean 类型 + * @return 存在bean对象, 不存在返回null + */ + public static T getExistBean(ApplicationContext applicationContext, Class aClass){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + String[] beanNamesForType = springBeanFactory.getBeanNamesForType(aClass, false, false); + if(beanNamesForType.length > 0){ + return springBeanFactory.getBean(aClass); + } else { + return null; + } + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext applicationContext + * @param beanName bean 名称 + * @param 返回的bean类型 + * @return 存在bean对象, 不存在返回null + */ + @SuppressWarnings("unchecked") + public static T getExistBean(ApplicationContext applicationContext, String beanName){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + if(springBeanFactory.containsBean(beanName)){ + Object bean = springBeanFactory.getBean(beanName); + return (T) bean; + } else { + return null; + } + } + + /** + * 获取存在的Bean。名称和类型任意批量即可返回 + * @param applicationContext applicationContext + * @param beanName bean名称 + * @param beanClass bean class + * @return T + * @param bean 类型 + */ + public static T getExistBean(ApplicationContext applicationContext, String beanName, Class beanClass){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + Map beansOfTypeMap = springBeanFactory.getBeansOfType(beanClass); + if(ObjectUtils.isEmpty(beansOfTypeMap)){ + return null; + } + Set> entries = beansOfTypeMap.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + T value = entry.getValue(); + if(Objects.equals(beanName, key) || Objects.equals(value.getClass().getName(), beanClass.getName())){ + return value; + } + } + return null; + } + + /** + * 通过注解获取bean + * @param applicationContext applicationContext + * @param annotationType 注解类型 + * @return List + */ + public static List getBeansWithAnnotation(ApplicationContext applicationContext, + Class annotationType){ + SpringBeanFactory springBeanFactory = applicationContext.getSpringBeanFactory(); + Map beanMap = springBeanFactory.getBeansWithAnnotation(annotationType); + return new ArrayList<>(beanMap.values()); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java new file mode 100755 index 0000000..5f4a956 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/utils/SpringBeanUtils.java @@ -0,0 +1,128 @@ +/** + * Copyright [2019-Present] [starBlues] + * + * Licensed 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 com.gitee.starblues.utils; + +import org.springframework.context.ApplicationContext; + +import java.lang.annotation.Annotation; +import java.util.*; + +/** + * 插件bean工具类 + * @author starBlues + * @since 3.0.0 + * @version 3.1.1 + */ +public class SpringBeanUtils { + + /** + * 获取bean名称 + * @param applicationContext ApplicationContext + * @return bean名称集合 + */ + public static Set getBeanName(ApplicationContext applicationContext){ + String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); + Set set = new HashSet<>(beanDefinitionNames.length); + set.addAll(Arrays.asList(beanDefinitionNames)); + return set; + } + + /** + * 得到ApplicationContext中的bean的实现 + * @param applicationContext ApplicationContext + * @param aClass 接口或者抽象类型bean类型 + * @param 接口或者抽象类型bean类型 + * @return 所有的实现对象 + */ + public static List getBeans(ApplicationContext applicationContext, Class aClass) { + Map beansOfTypeMap = applicationContext.getBeansOfType(aClass); + if(beansOfTypeMap.isEmpty()){ + return new ArrayList<>(); + } + return new ArrayList<>(beansOfTypeMap.values()); + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext ApplicationContext容器 + * @param aClass bean 类型 + * @param bean 类型 + * @return 存在bean对象, 不存在返回null + */ + public static T getExistBean(ApplicationContext applicationContext, Class aClass){ + String[] beanNamesForType = applicationContext.getBeanNamesForType(aClass, false, false); + if(beanNamesForType.length > 0){ + return applicationContext.getBean(aClass); + } else { + return null; + } + } + + /** + * 得到存在的bean, 不存在则返回null + * @param applicationContext ApplicationContext容器 + * @param beanName bean 名称 + * @param 返回的bean类型 + * @return 存在bean对象, 不存在返回null + */ + @SuppressWarnings("unchecked") + public static T getExistBean(ApplicationContext applicationContext, String beanName){ + if(applicationContext.containsBean(beanName)){ + Object bean = applicationContext.getBean(beanName); + return (T) bean; + } else { + return null; + } + } + + /** + * 获取存在的Bean。名称和类型任意批量即可返回 + * @param applicationContext applicationContext + * @param beanName bean名称 + * @param beanClass bean class + * @return T + * @param bean 类型 + */ + public static T getExistBean(ApplicationContext applicationContext, String beanName, Class beanClass){ + Map beansOfTypeMap = applicationContext.getBeansOfType(beanClass); + if(ObjectUtils.isEmpty(beansOfTypeMap)){ + return null; + } + Set> entries = beansOfTypeMap.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + T value = entry.getValue(); + if(beanName.equals(key) || Objects.equals(value.getClass().getName(), beanClass.getName())){ + return value; + } + } + return null; + } + + /** + * 通过注解获取bean + * @param applicationContext applicationContext + * @param annotationType 注解类型 + * @return List + */ + public static List getBeansWithAnnotation(ApplicationContext applicationContext, + Class annotationType){ + Map beanMap = applicationContext.getBeansWithAnnotation(annotationType); + return new ArrayList<>(beanMap.values()); + } + +} diff --git a/iot-spring-brick/spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json b/iot-spring-brick/spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100755 index 0000000..2b65e6d --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,157 @@ +{ + "groups": [ + { + "name": "plugin", + "type": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration" + } + ], + "properties": [ + { + "name": "plugin.enable", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "是否启用插件功能", + "defaultValue": true + }, + { + "name": "plugin.enableStarter", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "是否启用starter自动装配功能", + "defaultValue": true + }, + { + "name": "plugin.runMode", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "运行模式. 开发环境: dev; 生产/部署 环境: prod", + "defaultValue": "dev" + }, + { + "name": "plugin.mainPackage", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "主程序包名, 包名建议设置到范围最大级别, 主要用于插件依赖主程序时的类时, 进行包名匹配", + "defaultValue": true + }, + { + "name": "plugin.pluginPath", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "插件的路径. 开发环境下配置为插件模块上级目录; 生产环境下配置到插件jar包存放目录。建议配置绝对路径", + "defaultValue": "~/plugins/" + }, + { + "name": "plugin.backupPath", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "卸载插件后, 备份插件的目录" + }, + { + "name": "plugin.uploadTempPath", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "上传的插件所存储的临时目录" + }, + { + "name": "plugin.pluginRestPathPrefix", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "统一配置访问插件rest接口前缀", + "defaultValue": "/plugins" + }, + { + "name": "plugin.enablePluginIdRestPathPrefix", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "是否启用插件id作为rest接口前缀", + "defaultValue": true + }, + { + "name": "plugin.enablePluginIds", + "type": "java.util.Set", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "启用的插件id" + }, + { + "name": "plugin.disablePluginIds", + "type": "java.util.Set", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "禁用的插件id, 禁用后系统不会启动该插件, 如果禁用所有插件, 则Set集合中设置一个字符: *" + }, + { + "name": "plugin.sortInitPluginIds", + "type": "java.util.Set", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "设置初始化时插件启动的顺序" + }, + { + "name": "plugin.version", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "当前主程序的版本号, 用于校验插件是否可安装。插件中可通过插件配置信息 requires 来指定可安装的主程序版本。如果为: 0.0.0 的话, 表示不校验", + "defaultValue": "0.0.0" + }, + { + "name": "plugin.exactVersion", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "是否完全匹配版本。设置为true表示插件设置的requires的版本号完全匹配version版本号才可允许插件安装, 即: requires=x.y.z; 设置为false表示插件设置的requires的版本号小于等于version值, 插件就可安装, 即requires<=x.y.z", + "defaultValue": false + }, + { + "name": "plugin.pluginSwaggerScan", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "是否扫描插件 swagger 接口", + "defaultValue": true + }, + { + "name": "plugin.pluginFollowProfile", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "插件的配置文件 Profile 是否跟随主程序的 Profile 配置动态切换", + "defaultValue": false + }, + { + "name": "plugin.pluginFollowLog", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "插件日志打印是否跟随主程序", + "defaultValue": false + }, + { + "name": "plugin.decrypt", + "type": "com.gitee.starblues.integration.decrypt.DecryptConfiguration", + "sourceType": "com.gitee.starblues.integration.AutoIntegrationConfiguration", + "description": "对插件启动时进行解密校验配置。默认为不启用" + }, + { + "name": "plugin.decrypt.enable", + "type": "java.lang.Boolean", + "sourceType": "com.gitee.starblues.integration.decrypt.DecryptConfiguration", + "description": "解密校验配置是否启用。默认为启用", + "defaultValue": true + }, + { + "name": "plugin.decrypt.className", + "type": "java.lang.String", + "sourceType": "com.gitee.starblues.integration.decrypt.DecryptConfiguration", + "description": "解密实现类名称。默认: com.gitee.starblues.common.cipher.RsaPluginCipher", + "defaultValue": "com.gitee.starblues.common.cipher.RsaPluginCipher" + }, + { + "name": "plugin.decrypt.props", + "type": "java.lang.Map", + "sourceType": "com.gitee.starblues.integration.decrypt.DecryptConfiguration", + "description": "总配置" + }, + { + "name": "plugin.decrypt.plugins", + "type": "java.lang.List", + "sourceType": "com.gitee.starblues.integration.decrypt.DecryptConfiguration", + "description": "指定可生效的插件. 如果不配置, 则默认对全部插件生效" + } + ] +} \ No newline at end of file diff --git a/iot-spring-brick/spring-brick/src/main/resources/META-INF/spring.factories b/iot-spring-brick/spring-brick/src/main/resources/META-INF/spring.factories new file mode 100755 index 0000000..cc6e350 --- /dev/null +++ b/iot-spring-brick/spring-brick/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.gitee.starblues.integration.SpringBootPluginStarter \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3ccd34b..441d491 100755 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ cc.iotkit iot-iita-core - 1.0.1 + 1.0.2 pom @@ -23,6 +23,8 @@ iot-common-web iot-common-websocket iot-common-thing + iot-plugin-core + iot-spring-brick @@ -229,6 +231,12 @@ + + org.apache.xmlbeans + xmlbeans + 5.2.0 + + cn.dev33 sa-token-core