Repository: youtongluan/sumk Branch: master Commit: 14bb16252228 Files: 689 Total size: 1.5 MB Directory structure: gitextract_rcvshosm/ ├── CHANGELOG ├── LICENSE ├── README.md ├── async-logger/ │ ├── LICENSE │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ ├── slf4j/ │ │ │ │ ├── impl/ │ │ │ │ │ ├── StaticLoggerBinder.java │ │ │ │ │ └── StaticMDCBinder.java │ │ │ │ └── v2/ │ │ │ │ └── SumkServiceProvider.java │ │ │ └── yx/ │ │ │ └── log/ │ │ │ └── impl/ │ │ │ ├── CodeLine.java │ │ │ ├── CodeLineKit.java │ │ │ ├── DayRollingFileAppender.java │ │ │ ├── DefaultUnionLog.java │ │ │ ├── LeveledDayRollingFileAppender.java │ │ │ ├── LocalFileDao.java │ │ │ ├── LogAppendObserver.java │ │ │ ├── LogAppender.java │ │ │ ├── LogAppenderFactory.java │ │ │ ├── LogAppenders.java │ │ │ ├── LogHelper.java │ │ │ ├── LogObject.java │ │ │ ├── LogQueue.java │ │ │ ├── MonthRollingFileAppender.java │ │ │ ├── PlainOutput.java │ │ │ ├── PlainOutputImpl.java │ │ │ ├── RollingFileAppender.java │ │ │ ├── SumkLoggerFactory.java │ │ │ ├── SumkLoggerImpl.java │ │ │ ├── UnionLog.java │ │ │ ├── UnionLogDao.java │ │ │ ├── UnionLogObject.java │ │ │ ├── UnionLogObjectSerializer.java │ │ │ ├── UnionLogUtil.java │ │ │ └── UnionLogs.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.slf4j.spi.SLF4JServiceProvider │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── test/ │ │ ├── ProxyLog.java │ │ ├── Starter.java │ │ └── UnionDemo.java │ └── resources/ │ └── app.properties ├── pom.xml ├── sql.dtd ├── sumk-base/ │ ├── LICENSE │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── yx/ │ ├── annotation/ │ │ └── doc/ │ │ ├── Comment.java │ │ └── NotNull.java │ ├── base/ │ │ ├── Executable.java │ │ ├── ItemJoiner.java │ │ ├── Lifecycle.java │ │ ├── Ordered.java │ │ ├── StartOnceLifecycle.java │ │ ├── context/ │ │ │ ├── ActionContext.java │ │ │ ├── AppContext.java │ │ │ └── LogContext.java │ │ ├── date/ │ │ │ ├── DateAdapters.java │ │ │ ├── DateFormater.java │ │ │ ├── DateTimeFormater.java │ │ │ ├── DateTimeTypeAdapter.java │ │ │ ├── FullDateTimeFormater.java │ │ │ ├── SumkDateFormater.java │ │ │ ├── SumkDateQuery.java │ │ │ └── TimeUtil.java │ │ ├── matcher/ │ │ │ ├── BooleanMatcher.java │ │ │ ├── InOutMatcher.java │ │ │ ├── Matchers.java │ │ │ └── WildcardMatcher.java │ │ ├── scaner/ │ │ │ ├── ClassScaner.java │ │ │ ├── FileNameScaner.java │ │ │ └── JarFileUtil.java │ │ ├── sumk/ │ │ │ ├── UnmodifiableArrayList.java │ │ │ ├── UnsafeByteArrayOutputStream.java │ │ │ ├── UnsafeStringWriter.java │ │ │ └── map/ │ │ │ ├── ListEntrySet.java │ │ │ ├── ListMap.java │ │ │ └── UnmodifiableListMap.java │ │ └── thread/ │ │ ├── PriorityRunnable.java │ │ ├── SumkExecutorService.java │ │ ├── ThreadPools.java │ │ └── ThresholdExecutor.java │ ├── conf/ │ │ ├── AbsoluteXmlFilesLoader.java │ │ ├── AbstractFilesLoader.java │ │ ├── AbstractRefreshableSystemConfig.java │ │ ├── AppConfig.java │ │ ├── AppInfo.java │ │ ├── ClassPathXmlFilesLoader.java │ │ ├── ComposedConfig.java │ │ ├── Const.java │ │ ├── FileModifyTime.java │ │ ├── LocalMultiResourceLoaderSupplier.java │ │ ├── LocalhostUtil.java │ │ ├── MapConfig.java │ │ ├── MultiNodeConfig.java │ │ ├── MultiResourceLoader.java │ │ ├── RefreshableSystemConfig.java │ │ ├── SimpleBeanUtil.java │ │ ├── SystemConfig.java │ │ ├── SystemConfigHolder.java │ │ └── UrlSystemConfig.java │ ├── exception/ │ │ ├── BizException.java │ │ ├── CodeException.java │ │ ├── SimpleSumkException.java │ │ ├── SoaException.java │ │ ├── SumkException.java │ │ └── SumkExceptionCode.java │ ├── log/ │ │ ├── CodeLineMarker.java │ │ ├── ConsoleLog.java │ │ ├── DelegateLogger.java │ │ ├── Log.java │ │ ├── LogKits.java │ │ ├── LogLevel.java │ │ ├── LogSettings.java │ │ ├── Loggers.java │ │ ├── Logs.java │ │ ├── RawLog.java │ │ ├── SimpleLogger.java │ │ └── SumkLogger.java │ └── util/ │ ├── BitUtil.java │ ├── CollectionUtil.java │ ├── ExceptionUtil.java │ ├── FileUtil.java │ ├── IOUtil.java │ ├── Loader.java │ ├── StringUtil.java │ ├── SumkDate.java │ ├── SumkThreadPool.java │ ├── Task.java │ └── UUIDSeed.java ├── sumk-db/ │ ├── LICENSE │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── yx/ │ │ ├── annotation/ │ │ │ └── db/ │ │ │ ├── AutoCreateTime.java │ │ │ ├── Box.java │ │ │ ├── Column.java │ │ │ ├── SoftDelete.java │ │ │ └── Table.java │ │ └── db/ │ │ ├── DB.java │ │ ├── DBJson.java │ │ ├── SDB.java │ │ ├── conn/ │ │ │ ├── CommonConfigFactory.java │ │ │ ├── ConnectionPool.java │ │ │ ├── DBCPDataSourceFactory.java │ │ │ ├── DBConfig.java │ │ │ ├── DBConfigFactory.java │ │ │ ├── DSFactory.java │ │ │ ├── DataSourceFactory.java │ │ │ ├── DataSourceManager.java │ │ │ ├── DataSourceManagerImpl.java │ │ │ ├── DataSourceManagerSelector.java │ │ │ ├── DataSources.java │ │ │ ├── DefaultManagerSelector.java │ │ │ ├── HookContext.java │ │ │ ├── RouterFactory.java │ │ │ ├── SqlSessionHook.java │ │ │ ├── SumkConnection.java │ │ │ ├── SumkDataSource.java │ │ │ ├── WeightedDataSource.java │ │ │ └── WeightedRouterFactory.java │ │ ├── dao/ │ │ │ ├── AbstractCachable.java │ │ │ └── CountedResult.java │ │ ├── enums/ │ │ │ ├── CacheType.java │ │ │ ├── ColumnType.java │ │ │ ├── DBType.java │ │ │ ├── TransactionType.java │ │ │ ├── TxHook.java │ │ │ └── ValidRecord.java │ │ ├── event/ │ │ │ ├── DBEvent.java │ │ │ ├── DBEventPublisher.java │ │ │ ├── DeleteEvent.java │ │ │ ├── EventLane.java │ │ │ ├── InsertEvent.java │ │ │ ├── ModifyEvent.java │ │ │ ├── QueryEvent.java │ │ │ └── UpdateEvent.java │ │ ├── exec/ │ │ │ ├── BoxAopExecutorSupplier.java │ │ │ ├── DBExecutor.java │ │ │ ├── DBSource.java │ │ │ ├── DBSources.java │ │ │ ├── DBTransaction.java │ │ │ └── DefaultDBSource.java │ │ ├── kit/ │ │ │ ├── DBKits.java │ │ │ └── SDBuilder.java │ │ ├── listener/ │ │ │ ├── DeleteListener.java │ │ │ ├── InsertListener.java │ │ │ ├── SelectListener.java │ │ │ └── UpdateListener.java │ │ ├── log/ │ │ │ └── SimpleSqlLogImpl.java │ │ ├── mapper/ │ │ │ ├── DBPlugin.java │ │ │ ├── ForeachParser.java │ │ │ ├── IFParser.java │ │ │ ├── ItemsParser.java │ │ │ ├── JoinerFactory.java │ │ │ ├── NamedExecutor.java │ │ │ ├── PureStringParser.java │ │ │ ├── RawExecutor.java │ │ │ ├── SqlHolder.java │ │ │ ├── SqlParser.java │ │ │ ├── SqlParsers.java │ │ │ ├── SqlXmlBuilderFactory.java │ │ │ └── SqlXmlParser.java │ │ ├── monitor/ │ │ │ └── DBMonitor.java │ │ ├── spec/ │ │ │ ├── BoxSpec.java │ │ │ ├── ColumnSpec.java │ │ │ ├── DBSpecs.java │ │ │ └── TableSpec.java │ │ ├── sql/ │ │ │ ├── AbstractOperationGroup.java │ │ │ ├── AbstractSqlBuilder.java │ │ │ ├── ColumnMeta.java │ │ │ ├── ColumnOperation.java │ │ │ ├── CompareOperation.java │ │ │ ├── Count.java │ │ │ ├── DBFactory.java │ │ │ ├── DBFlag.java │ │ │ ├── DBNameResolvers.java │ │ │ ├── DBSettings.java │ │ │ ├── DBSupplier.java │ │ │ ├── DBSupplierImpl.java │ │ │ ├── Delete.java │ │ │ ├── EstimateVisitCounter.java │ │ │ ├── GroupAND.java │ │ │ ├── GroupOR.java │ │ │ ├── InnerDelete.java │ │ │ ├── Insert.java │ │ │ ├── InsertResult.java │ │ │ ├── MapedSql.java │ │ │ ├── MapedSqlBuilder.java │ │ │ ├── ModifySqlBuilder.java │ │ │ ├── Operation.java │ │ │ ├── PojoMeta.java │ │ │ ├── PojoMetaHolder.java │ │ │ ├── RawSqlBuilder.java │ │ │ ├── Select.java │ │ │ ├── SelectBuilder.java │ │ │ ├── SoftDeleteMeta.java │ │ │ ├── SoftDeleteParser.java │ │ │ ├── SoftDeleteParserImpl.java │ │ │ ├── SqlBuilder.java │ │ │ ├── SqlLog.java │ │ │ ├── TableBootWatcher.java │ │ │ ├── Update.java │ │ │ ├── VisitCounter.java │ │ │ └── token/ │ │ │ ├── MapValueHandler.java │ │ │ ├── MapedSqlTokenParser.java │ │ │ ├── ReplaceTokenHandler.java │ │ │ ├── StringTokenParser.java │ │ │ └── VariableTokenHandler.java │ │ ├── sumk/ │ │ │ └── batis/ │ │ │ ├── ConfigurationFactory.java │ │ │ ├── ProxySession.java │ │ │ ├── SqlSessionFactory.java │ │ │ └── SqlSessionHolder.java │ │ └── visit/ │ │ ├── Exchange.java │ │ ├── MapResultHandler.java │ │ ├── PojoResultHandler.java │ │ ├── RecordAccess.java │ │ ├── RecordRepository.java │ │ ├── RedisAccess.java │ │ ├── ResultHandler.java │ │ ├── ResultSetUtils.java │ │ ├── SumkDbVisitor.java │ │ ├── SumkStatement.java │ │ └── Visitors.java │ └── resources/ │ └── META-INF/ │ ├── sql.dtd │ └── sumk.factories ├── sumk-framework/ │ ├── LICENSE │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── yx/ │ │ ├── annotation/ │ │ │ ├── Bean.java │ │ │ ├── ConditionOnProperty.java │ │ │ ├── Exclude.java │ │ │ ├── ExcludeFromParams.java │ │ │ ├── ExcludeFromResponse.java │ │ │ ├── Inject.java │ │ │ ├── Param.java │ │ │ ├── Priority.java │ │ │ └── spec/ │ │ │ ├── BeanSpec.java │ │ │ ├── BuiltInParsers.java │ │ │ ├── InjectSpec.java │ │ │ ├── ParamSpec.java │ │ │ ├── SpecParsers.java │ │ │ └── Specs.java │ │ ├── bean/ │ │ │ ├── BeanAssemblerBootWatcher.java │ │ │ ├── BeanFieldFinder.java │ │ │ ├── BeanKit.java │ │ │ ├── BeanPool.java │ │ │ ├── BeanProvider.java │ │ │ ├── BeanRegistry.java │ │ │ ├── Booter.java │ │ │ ├── Boxed.java │ │ │ ├── ComplexBean.java │ │ │ ├── DefaultBeanFieldFinder.java │ │ │ ├── FactoryBean.java │ │ │ ├── IOC.java │ │ │ ├── InnerIOC.java │ │ │ ├── InnerProvider.java │ │ │ ├── InterfaceBean.java │ │ │ ├── NameSlot.java │ │ │ ├── NamedBean.java │ │ │ ├── ParallelBootWatcher.java │ │ │ ├── Plugin.java │ │ │ ├── PluginBooter.java │ │ │ ├── aop/ │ │ │ │ ├── AopContext.java │ │ │ │ ├── AopExecutor.java │ │ │ │ ├── AopExecutorChain.java │ │ │ │ ├── AopExecutorManager.java │ │ │ │ ├── AopExecutorSupplier.java │ │ │ │ ├── asm/ │ │ │ │ │ ├── AsmUtils.java │ │ │ │ │ ├── MethodInfoClassVisitor.java │ │ │ │ │ ├── MethodParamInfo.java │ │ │ │ │ ├── MethodPojo.java │ │ │ │ │ ├── ParamPojo.java │ │ │ │ │ ├── ParamPojoClassFactory.java │ │ │ │ │ ├── ParamPojos.java │ │ │ │ │ ├── ParseParamsMethodVisitor.java │ │ │ │ │ ├── ProxyClassLoader.java │ │ │ │ │ ├── ProxyClassVistor.java │ │ │ │ │ ├── ProxyMethodWritor.java │ │ │ │ │ └── WriterHelper.java │ │ │ │ └── context/ │ │ │ │ ├── CalleeNode.java │ │ │ │ └── NodeContext.java │ │ │ └── watcher/ │ │ │ ├── BeanCreateWatcher.java │ │ │ ├── BeanInjectWatcher.java │ │ │ └── BootWatcher.java │ │ ├── common/ │ │ │ ├── Host.java │ │ │ ├── Predicator.java │ │ │ ├── StringEntity.java │ │ │ ├── action/ │ │ │ │ ├── ActInfoUtil.java │ │ │ │ ├── ActionStatis.java │ │ │ │ ├── ActionStatisImpl.java │ │ │ │ ├── ParamDescript.java │ │ │ │ └── StatisItem.java │ │ │ ├── expression/ │ │ │ │ ├── AndExpression.java │ │ │ │ ├── Expressions.java │ │ │ │ ├── HasKeyExpression.java │ │ │ │ ├── MatchType.java │ │ │ │ ├── NotEmptyExpression.java │ │ │ │ ├── NotNullExpression.java │ │ │ │ ├── OrExpression.java │ │ │ │ └── SimpleExpression.java │ │ │ ├── json/ │ │ │ │ ├── ByteArrayTypeAdapter.java │ │ │ │ ├── GsonHelper.java │ │ │ │ ├── GsonOperator.java │ │ │ │ ├── JsonOperator.java │ │ │ │ ├── JsonTypes.java │ │ │ │ ├── ParamPojoTypeAdapter.java │ │ │ │ ├── ParamPojoTypeAdapterFactory.java │ │ │ │ └── ServerJsonExclusionStrategy.java │ │ │ ├── listener/ │ │ │ │ ├── ConcurrentSumkListener.java │ │ │ │ ├── EventBus.java │ │ │ │ ├── EventBusFactory.java │ │ │ │ └── SumkListener.java │ │ │ ├── locale/ │ │ │ │ ├── I18n.java │ │ │ │ ├── I18nBuilder.java │ │ │ │ ├── I18nMessageProvider.java │ │ │ │ └── I18nMessageProviderImpl.java │ │ │ ├── lock/ │ │ │ │ ├── Lock.java │ │ │ │ ├── Locked.java │ │ │ │ ├── Locker.java │ │ │ │ └── SLock.java │ │ │ ├── monitor/ │ │ │ │ ├── MessageProvider.java │ │ │ │ └── Monitors.java │ │ │ ├── route/ │ │ │ │ ├── AbstractWeightedServer.java │ │ │ │ ├── EmptyRouter.java │ │ │ │ ├── Router.java │ │ │ │ ├── Routes.java │ │ │ │ ├── SingleRouter.java │ │ │ │ ├── WeightedRouter.java │ │ │ │ └── WeightedServer.java │ │ │ ├── sequence/ │ │ │ │ ├── AbstractSeq.java │ │ │ │ ├── LongTermSeqImpl.java │ │ │ │ ├── Seq.java │ │ │ │ ├── SeqCounter.java │ │ │ │ ├── SeqHolder.java │ │ │ │ ├── SeqImpl.java │ │ │ │ └── SnowflakeCounter.java │ │ │ ├── util/ │ │ │ │ ├── S.java │ │ │ │ ├── SBuilder.java │ │ │ │ ├── SeqUtil.java │ │ │ │ ├── helper/ │ │ │ │ │ └── ArrayHelper.java │ │ │ │ ├── kit/ │ │ │ │ │ ├── Asserts.java │ │ │ │ │ ├── BeanConverter.java │ │ │ │ │ ├── PriorityKits.java │ │ │ │ │ └── TypeConverter.java │ │ │ │ └── secury/ │ │ │ │ ├── AESEncryptor.java │ │ │ │ ├── Base64.java │ │ │ │ ├── Base64Impl.java │ │ │ │ ├── CommonDigest.java │ │ │ │ ├── Encryptor.java │ │ │ │ └── Hasher.java │ │ │ └── validate/ │ │ │ ├── AbstractParamInfo.java │ │ │ ├── ComplexParamValidator.java │ │ │ ├── FieldParameterHolder.java │ │ │ ├── FieldParameterInfo.java │ │ │ ├── InvalidParamException.java │ │ │ ├── ManuParameterInfo.java │ │ │ ├── ParamInfo.java │ │ │ ├── ParameterInfo.java │ │ │ ├── SimpleParamValidator.java │ │ │ ├── Validator.java │ │ │ └── Validators.java │ │ ├── main/ │ │ │ ├── StartConstants.java │ │ │ └── SumkServer.java │ │ └── redis/ │ │ ├── Checkable.java │ │ ├── Redis.java │ │ ├── RedisChecker.java │ │ ├── RedisConfig.java │ │ ├── RedisCounter.java │ │ ├── RedisFactory.java │ │ ├── RedisLoader.java │ │ ├── RedisPlugin.java │ │ ├── RedisPool.java │ │ ├── RedisSettings.java │ │ ├── RedisType.java │ │ ├── command/ │ │ │ ├── BinaryJedisCommand.java │ │ │ ├── JedisCommand.java │ │ │ ├── MultiKeyCommand.java │ │ │ └── ScriptingCommand.java │ │ └── v3/ │ │ ├── AbstractJedisExecutor.java │ │ ├── AbstractRedis.java │ │ ├── JedisExecutor.java │ │ ├── Redis3Factory.java │ │ ├── Redis3Kit.java │ │ ├── Redis3x.java │ │ ├── RedisCluster.java │ │ ├── RedisImpl.java │ │ ├── SentinelJedisExecutor.java │ │ └── SingleJedisExecutor.java │ └── resources/ │ └── META-INF/ │ ├── lua_del │ └── sumk.factories ├── sumk-http/ │ ├── LICENSE │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── yx/ │ │ ├── annotation/ │ │ │ └── http/ │ │ │ ├── SumkFilter.java │ │ │ ├── SumkServlet.java │ │ │ ├── Upload.java │ │ │ └── Web.java │ │ └── http/ │ │ ├── HttpEncryptor.java │ │ ├── HttpErrorCode.java │ │ ├── HttpHeaderName.java │ │ ├── HttpJson.java │ │ ├── HttpPlugin.java │ │ ├── MessageType.java │ │ ├── Signer.java │ │ ├── WebFilter.java │ │ ├── WebUtil.java │ │ ├── act/ │ │ │ ├── AbstractActionInfo.java │ │ │ ├── ActNameResolver.java │ │ │ ├── HttpActionInfo.java │ │ │ ├── HttpActionNode.java │ │ │ ├── HttpActions.java │ │ │ ├── IgnoreNameActionInfo.java │ │ │ └── PrefixMappingActionInfo.java │ │ ├── handler/ │ │ │ ├── AbstractHttpHandler.java │ │ │ ├── Base64DecodeHandler.java │ │ │ ├── Base64EncodeHandler.java │ │ │ ├── DecryptHandler.java │ │ │ ├── EncryptHandler.java │ │ │ ├── HttpHandler.java │ │ │ ├── HttpHandlerChain.java │ │ │ ├── InvokeHandler.java │ │ │ ├── MultiItemImpl.java │ │ │ ├── MultipartHandler.java │ │ │ ├── MultipartHolder.java │ │ │ ├── MultipartItem.java │ │ │ ├── ReqDataHandler.java │ │ │ ├── ReqSignValidateHandler.java │ │ │ ├── ReqToStringHandler.java │ │ │ ├── ReqUserHandler.java │ │ │ ├── RespBodyHandler.java │ │ │ ├── RespToStringHandler.java │ │ │ ├── RestType.java │ │ │ ├── ToBytesHandler.java │ │ │ └── WebContext.java │ │ ├── invoke/ │ │ │ ├── WebHandler.java │ │ │ ├── WebVisitor.java │ │ │ └── WebVisitorImpl.java │ │ ├── kit/ │ │ │ ├── DefaultHttpEncryptor.java │ │ │ ├── DefaultHttpKit.java │ │ │ ├── HttpCiphers.java │ │ │ ├── HttpKit.java │ │ │ ├── HttpSettings.java │ │ │ ├── InnerHttpUtil.java │ │ │ └── LocalWebContext.java │ │ ├── log/ │ │ │ ├── HttpLogHandler.java │ │ │ ├── HttpLogs.java │ │ │ └── PlainHttpLogHandler.java │ │ ├── monitor/ │ │ │ └── HttpMonitors.java │ │ ├── select/ │ │ │ ├── DefaultHttpActionSelector.java │ │ │ └── HttpActionSelector.java │ │ ├── server/ │ │ │ ├── AbstractActionServer.java │ │ │ ├── AbstractCommonHttpServlet.java │ │ │ ├── DocumentServlet.java │ │ │ ├── HttpMethod.java │ │ │ ├── MultipartServer.java │ │ │ ├── RestServer.java │ │ │ └── SumkMonitor.java │ │ ├── spec/ │ │ │ ├── HttpSpecs.java │ │ │ ├── SumkFilterSpec.java │ │ │ ├── SumkServletSpec.java │ │ │ ├── UploadSpec.java │ │ │ └── WebSpec.java │ │ ├── start/ │ │ │ ├── JettyHandlerSupplier.java │ │ │ ├── JettyHttpsServer.java │ │ │ ├── JettyServer.java │ │ │ ├── JettyServerConnector.java │ │ │ ├── SumkLoaderListener.java │ │ │ └── WebAnnotationResolver.java │ │ └── user/ │ │ ├── AbstractLoginServlet.java │ │ ├── AbstractUserSession.java │ │ ├── CacheHelper.java │ │ ├── HttpLoginWrapper.java │ │ ├── HttpSessionFactory.java │ │ ├── LocalUserSession.java │ │ ├── LoginObject.java │ │ ├── LoginServlet.java │ │ ├── RemoteUserSession.java │ │ ├── SessionObject.java │ │ ├── TimedCachedObject.java │ │ ├── UserSession.java │ │ └── WebSessions.java │ └── resources/ │ └── META-INF/ │ ├── http/ │ │ └── listeners │ └── sumk.factories ├── sumk-rpc/ │ ├── LICENSE │ ├── pom.xml │ ├── rpc.md │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── yx/ │ │ ├── annotation/ │ │ │ └── rpc/ │ │ │ ├── Soa.java │ │ │ ├── SoaClass.java │ │ │ └── SoaClientConfig.java │ │ └── rpc/ │ │ ├── BusinessHandler.java │ │ ├── Profile.java │ │ ├── RpcErrorCode.java │ │ ├── RpcJson.java │ │ ├── RpcSettings.java │ │ ├── RpcUtil.java │ │ ├── client/ │ │ │ ├── AbstractRpcFuture.java │ │ │ ├── AbstractTransportClient.java │ │ │ ├── Client.java │ │ │ ├── ClientHandler.java │ │ │ ├── ErrorRpcFuture.java │ │ │ ├── HostChecker.java │ │ │ ├── LockHolder.java │ │ │ ├── ParamType.java │ │ │ ├── Req.java │ │ │ ├── Rpc.java │ │ │ ├── RpcCallInfo.java │ │ │ ├── RpcFuture.java │ │ │ ├── RpcFutureImpl.java │ │ │ ├── RpcLocker.java │ │ │ ├── RpcResult.java │ │ │ ├── TransportClientHolder.java │ │ │ └── intf/ │ │ │ ├── IntfClientHandler.java │ │ │ ├── InvocationHandlerFactory.java │ │ │ ├── SoaClientFactory.java │ │ │ └── SoaClientPlugin.java │ │ ├── codec/ │ │ │ ├── AbstractDataBuffer.java │ │ │ ├── CodecKit.java │ │ │ ├── DataStream.java │ │ │ ├── Protocols.java │ │ │ ├── ReqParamType.java │ │ │ ├── Request.java │ │ │ ├── StreamAble.java │ │ │ ├── decoders/ │ │ │ │ ├── DataDecoder.java │ │ │ │ ├── RequestDecoder.java │ │ │ │ └── ResponseDecoder.java │ │ │ └── encoders/ │ │ │ ├── AbstractEncoder.java │ │ │ ├── DataEncoder.java │ │ │ └── StreamAbleEncoder.java │ │ ├── context/ │ │ │ ├── InnerRpcUtil.java │ │ │ ├── RpcActionNode.java │ │ │ └── RpcActions.java │ │ ├── data/ │ │ │ ├── ApiProfile.java │ │ │ ├── RouteDataOperator.java │ │ │ ├── RouteDataOperatorImpl.java │ │ │ ├── RouteDataOperators.java │ │ │ ├── RouteInfo.java │ │ │ └── RoutePathData.java │ │ ├── log/ │ │ │ ├── PlainRpcLogHandler.java │ │ │ ├── RpcLog.java │ │ │ ├── RpcLogHandler.java │ │ │ └── RpcLogs.java │ │ ├── monitor/ │ │ │ ├── RpcActionProvider.java │ │ │ └── RpcMonitor.java │ │ ├── netty/ │ │ │ ├── DefaultChannelInitializerSupplier.java │ │ │ ├── NettyChannel.java │ │ │ ├── NettyClient.java │ │ │ ├── NettyConfigSetter.java │ │ │ ├── NettyDataBuffer.java │ │ │ ├── NettyDecoder.java │ │ │ ├── NettyEncoder.java │ │ │ ├── NettyHandler.java │ │ │ ├── NettyKit.java │ │ │ ├── NettyServer.java │ │ │ ├── NettyTransportFactory.java │ │ │ └── NettyWriteFuture.java │ │ ├── registry/ │ │ │ ├── RegistryConst.java │ │ │ ├── RegistryFactory.java │ │ │ ├── client/ │ │ │ │ ├── RegistryClient.java │ │ │ │ ├── RouteEvent.java │ │ │ │ ├── RouteEventType.java │ │ │ │ ├── RouterHolder.java │ │ │ │ ├── RpcRoutes.java │ │ │ │ └── WeightedHost.java │ │ │ ├── server/ │ │ │ │ ├── RegistryHelper.java │ │ │ │ └── RegistryServer.java │ │ │ └── zookeeper/ │ │ │ ├── SimpleZkSerializer.java │ │ │ ├── ZKSystemConfig.java │ │ │ ├── ZkClientHelper.java │ │ │ ├── ZkRegistryClient.java │ │ │ ├── ZkRegistryFactory.java │ │ │ └── ZkRegistryServer.java │ │ ├── server/ │ │ │ ├── LocalRequestHandler.java │ │ │ ├── LocalRpcContext.java │ │ │ ├── RequestHandler.java │ │ │ ├── Response.java │ │ │ ├── RpcContext.java │ │ │ ├── RpcFilter.java │ │ │ ├── ServerHandler.java │ │ │ ├── SoaPlugin.java │ │ │ ├── impl/ │ │ │ │ ├── JsonedParamReqHandler.java │ │ │ │ ├── OrderedParamReqHandler.java │ │ │ │ ├── RpcHandler.java │ │ │ │ └── ServerExceptionHandler.java │ │ │ └── start/ │ │ │ ├── SoaAnnotationResolver.java │ │ │ ├── SoaClassResolver.java │ │ │ ├── SoaClassResolverImpl.java │ │ │ ├── SoaNameResolver.java │ │ │ ├── SoaNameResolverImpl.java │ │ │ └── SoaServer.java │ │ ├── spec/ │ │ │ ├── RpcSpecs.java │ │ │ ├── SoaClassSpec.java │ │ │ ├── SoaClientConfigSpec.java │ │ │ └── SoaSpec.java │ │ └── transport/ │ │ ├── DataBuffer.java │ │ ├── RpcWriteFuture.java │ │ ├── RpcWriteListener.java │ │ ├── TransportChannel.java │ │ ├── TransportClient.java │ │ ├── TransportFactory.java │ │ ├── TransportServer.java │ │ └── Transports.java │ └── resources/ │ └── META-INF/ │ └── sumk.factories ├── sumk-rpc-mina/ │ ├── LICENSE │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── yx/ │ │ │ └── rpc/ │ │ │ └── mina/ │ │ │ ├── MinaChannel.java │ │ │ ├── MinaClient.java │ │ │ ├── MinaDataBuffer.java │ │ │ ├── MinaHandler.java │ │ │ ├── MinaKit.java │ │ │ ├── MinaProtocolDecoder.java │ │ │ ├── MinaProtocolEncoder.java │ │ │ ├── MinaServer.java │ │ │ ├── MinaTransportFactory.java │ │ │ └── MinaWriteFuture.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── sumk.factories │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── test/ │ │ ├── Main.java │ │ ├── RpcPressTest.java │ │ ├── RpcTest.java │ │ ├── inner/ │ │ │ └── pojo/ │ │ │ ├── DemoUser.java │ │ │ └── Multikey.java │ │ └── soa/ │ │ ├── demo/ │ │ │ └── EchoAction.java │ │ └── server/ │ │ └── SOAServer.java │ └── resources/ │ └── app.properties └── sumk-test/ ├── LICENSE ├── pom.xml ├── src/ │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── test/ │ │ ├── Main.java │ │ ├── client/ │ │ │ ├── HttpAesClientTest.java │ │ │ ├── HttpPlainClientTest.java │ │ │ ├── HttpPressTest.java │ │ │ ├── RpcPressTest.java │ │ │ └── RpcTest.java │ │ ├── inner/ │ │ │ ├── dao/ │ │ │ │ ├── DemoUserDao.java │ │ │ │ ├── DemoUserDaoImpl.java │ │ │ │ ├── DemoUserMybatisDao.java │ │ │ │ ├── LocalSqlDao.java │ │ │ │ └── MultikeyDao.java │ │ │ ├── po/ │ │ │ │ ├── DemoUser.java │ │ │ │ └── Multikey.java │ │ │ ├── soa/ │ │ │ │ ├── demo/ │ │ │ │ │ └── EchoAction.java │ │ │ │ └── server/ │ │ │ │ └── SOAServer.java │ │ │ └── web/ │ │ │ ├── client/ │ │ │ │ └── Encrypt.java │ │ │ └── demo/ │ │ │ ├── AesTestServer.java │ │ │ ├── DBDemo.java │ │ │ ├── DemoSessionObject.java │ │ │ ├── MyLoginServlet.java │ │ │ └── PlainServer.java │ │ ├── orm/ │ │ │ ├── BaseOrmTest.java │ │ │ ├── MultiPrimaryTest.java │ │ │ ├── SinglePrimaryTest.java │ │ │ └── SqlTest.java │ │ └── token/ │ │ ├── MapedSqlTokenParserTest.java │ │ └── StringTokenParserTest.java │ └── resources/ │ ├── app.properties │ └── sql/ │ ├── test.xml │ └── user/ │ └── demo.xml └── test.sql ================================================ FILE CONTENTS ================================================ ================================================ FILE: CHANGELOG ================================================ v2.3.1 1、将JsonUtil改为S.json 2、sdb和mybatis的配置文件可以打包到jar中 v2.3.2 1、增加jetty端口被占用时的重试功能,默认重试将近2分钟。通过http.bind.retry=0可以关闭重试功能。 2、sdb的xml配置文件中,dtd不再通过网络获取。 3、去掉MD5Util和S.md5,增加S.hasher 4、去掉EncryUtil,增加S.encryptor v2.3.3 1、增加redis的存活检测,在redis无法连接的时候,快速失败。 2、增加工具类S.beans的方法 v2.3.4 1、增加RedisConfig工具类 2、因为Observer接口从java9开始被标注为不推荐使用。所以AppInfo中使用Consumer代替Observer接口 3、增加SimpleLogger接口和SimpleLoggerHolder类。这个是配置等跟日志同级的模块使用的 4、允许soa和web使用自定义的线程池 5、重构主键生成的代码 6、支持在微服务中配置随机端口。端口值为0表示随机端口 7、修复鹰眼跟踪的bug 8、将分布式锁的入口改到S类中 v2.3.5 1、修复日志级别的一个bug 2、修改分布式锁的类名称 v2.3.6 1、修复orm事件的bug v2.3.7 1、限制rest接口只支持get和post,其中get方法仅用于测试,它有可能使用浏览器缓存 2、将Base64Util改为S.base64,并且修改了工具类S中许多工具的名称 3、ThreadContext改名为ActionContext v2.3.8 1、去掉对dbcp2连接池的硬编码依赖 2、几个名称微调 3、将日志的代码行参数默认值改为true v2.3.9 1、AbstractSessionServlet改名为AbstractLoginServlet,并且添加了一个方法 2、整理日志的模块名 3、去掉Log.printStack(Throwable e)方法 4、Transaction的NORMAL改名为REQUIRED,EMBED改名为REQUIRES_NEW v2.4.0 1、在配置文件中,soa开头的改为sumk.rpc开头。http开头的改为sumk.http开头 2、将RedisCallBack等单方法的接口改为java自带的接口 3、md5的结果从原来的大写方式改为小写方式 v2.4.1 1、将Class.newInstance()改为使用构造器初始化,这是因为java12开始,该方法被列为不推荐使用 2、http和微服务的名称支持逗号分隔 3、日志level的分隔符,从原来的逗号扩展到逗号和分号都可以 v2.4.2 1、S.base64增加2个小方法 2、SystemConfig.keys()的返回值,从Collection改为Set v2.5.0 1、ORM去掉了withnull属性,改为自动判断。如果开发者传递的参数是pojo对象,会将pojo中null字段排除掉,如果传递的是map,其中的null字段会被保留。比如作为where条件,它将会作为IS NULL条件 2、ORM中的update去掉根据redis主键更新的方法 3、S.bean去掉无参的beanToMap方法。 4、Plugin接口的stop方法提供默认方法。 5、HttpUtil增加获取request和response的方法 6、优化字节码处理 v2.5.1 1、优化分布式session的存取性能 2、去掉http对redis的显式依赖 3、去掉session中对象的修改功能 4、去掉session根据不同客户端类型定制超时时间的功能 v2.5.2 1、去掉sumk.log.type参数,如果要切换日志类型需要手工调用api(这个是冷门需求) 2、将app.properties的获取机制简单化 v2.5.3 1、将HttpUtil改名为WebUtil 2、允许动态调整核心线程池的容量 3、去掉SimpleBizException类,将BizException的构造方法改为私有,改为使用create方法创建 v2.6.0 1、重构数据库连接的获取方式,增加读连接的升级功能 2、提供默认的微服务日志 v2.6.1 1、优化微服务的日志 2、数据库连接AutoCommit的控制从连接池改为框架内置控制 3、去掉了Cached注解及相关功能 v2.6.2 1、按参数顺序调用的Rpc接口,允许接口发布后再添加参数 2、限制日志body的长度,默认1000,后面用省略号替代 v2.6.3 1、SumkDate的毫秒时间,允许末尾的0省略掉(如果全是0,保留1个就行) 2、系统启动的时候,将原来一些同步启动的方法改为异步多线程启动,加快启动速度 3、改进本机ip的获取方式 v2.6.4 1、Select增加不等于比较符 v2.6.5 1、 select增加对IS NOT NULL的支持 2、修改web和rpc拦截器接口的定义 3、添加web是否支持get请求的开关 4、将后台任务线程改为正常模式,然后去除了SumkServer.main()的无限阻塞 5、添加web接口名是否大小写敏感的开关,默认铭感。 v2.6.6 1、优化http的日志功能 v2.6.7 1、http允许开发者指定默认接口,如果接口找不到就会进入该接口 2、允许通过sumk.redis.disconnect.null配置,指定在redis服务器异常的时候,是抛异常还是返回null 3、通过sumk.redis.conf.**来配置redis连接池的默认属性 v2.7.0 1、精简redis接口,并去掉与jedis包的直接耦合 2、修改redis的配置方式 3、优化日志级别判定的性能 4、对依赖包的版本做了一次集体升级 v2.7.1 1、修复sumk-log与spring boot、阿波罗配置中心一起使用时,spring日志的堆栈溢出问题 v2.7.2 1、在2.7.0中,将jedis升级到2.10.x后,之前版本的jedis需要手工构建JedisPool对象。现修复这个问题 2、@Box事务支持AutoCommit模式 3、只是Pojo使用byte[]和String映射Blob和Clob类型 4、修改@Column、@Param的一些属性名称 v2.7.3 1、增加工具类M,一般用于消息提示,通过与AppInfo的搭配,也可以实现国际化的目的 2、增加@RequestBody注解,用于支持http的流传输 3、去掉了http的客户类型功能(即@Web的type),因为这个功能感觉很鸡肋 4、增加类似dubbo那种,直接用接口注册和调用的微服务。目前对泛型返回值支持不好,如有需要,需借助JsonTypes工具 v2.7.5 1、可以统一对http服务的出错信息进行定制 2、当s.log.xxx的配置只有一个路径时,path:这个前缀可以省略 3、当web服务没有定义入参的时候,可以通过HttpServletRequest的InputStream来读取输入流 v.2.7.6 1、将工具类S的属性改为方法 2、AES默认加密方式改为CBC 3、默认的配置管理增加对System参数的读取,但不监听System中参数的变更 v2.7.7 1、新增@CreateTime注解,它的作用是新增记录的时候,会自动传入创建时间,并且与id保持一致 2、统一日志独立为特殊日志,并且有纯配置方式改为代码与配置相结合的方式 3、优化了日志输出的方式 4、优化了作业调度并提供Task类 5、redis支持原生cluster模式 6、去掉了groupId。appId使用分隔符方式可以起到groupId一样的效果 v2.7.8 1、增加慢sql的耗时日志 v2.7.9 1、SDB的xml增加items标签 2、SDB增加builder方法 v2.8.0 1、rpc的协议做了变更,不能与2.8之前的微服务进行交互 2、优化SumkDate性能 3、WebFileter和RpcFilter调用下一个Filter方法改为callNextFilter 4、Redis增加mute()方法,调用之后,如果发生异常,会用null来代替抛出异常 v2.8.1 1、文件上传改为基于servlet3的MultiPart模式,而不是commons-upload。同时去掉对commons-upload的依赖。相应的,WebUtil的getUploadFiles()改名为getMultiParts,UploadFile类改为MultipartItem 2、StreamUtil的extractData改名为readAllBytes v2.8.2 1、@Web的requestEncrypt改为requestType,responseEncrypt改为responseType。对应的EncryptType类型改名为MessageType,NONE属性改名为PLAIN,AES改名为ENCRYPT 2、去掉@ErrorHandler,这个用法较冷门,通过WebFilter接口也能轻松实现 3、LoginObject的error方法改名为fail 4、提供加密接口的明文调试功能 v2.8.3 1、sumk.http.fusing支持头尾*匹配,并且放宽对接口书写格式的要求 2、移除CollectionUtil.unmodifyList()方法移除第二个参数 3、SDB的selectOne改名为queryOne 4、@CreateTime改名为@AutoCreateTime,并且无论是否主键是自动生成的,该注解都生效 5、Select的byPrimaryId方法改名为byDatabaseId 6、DBType和TransactionType改了下包名位置 7、web响应增加s-trace头部,它用于跟踪日志 v2.8.4 1、统一日志增加sumk.unionlog.exclude参数,并修复异常信息打印的问题 2、增加SumkExceptionCode接口,用于定义一些重要的框架异常码,方便开发者提示错误信息 v2.8.5 1、s.log.day等日志配置增加exclude过滤条件 2、@Param参数增加对空字符串的过滤 v2.9.0 1、zk和http方式的统一配置支持多节点模式 2、http错误码由原来的499改为550 3、个别地方性能优化 v2.9.1 1、DB部分更新支持字段为null 2、支持设置用户session最长的存活时间 v2.9.2 1、登录时的加密key改为通过header传递 2、修复Select中缓存跟数据库数据重复的bug 3、lock增加动态设置锁超时的功能 4、@Web添加tags属性 v2.9.3 1、SumkDate支持在DB和SDB中使用,但不支持mbatis、hibernate等外置数据库操作 2、支持使用配置的方式替代@SoaClass注解 3、S.bean()支持日期类型、数字类型的模糊匹配 4、@Web添加method属性 5、SumkServer.main增加class参数,以支持sumk.jar和工程代码不在一个classloader里 v2.9.4 1、@Param支持pojo以及数组方式 2、BizException允许通过配置输出堆栈 3、WebUtil和RpcUtil提供所有参数组成的map,并且map的遍历顺序与参数顺序一致 4、通过sumk.db.default可以配置默认的数据库名称 5、修改SDB的包名 v2.9.5 1、SDB的count改为返回long 2、s.http.response.header.开头的配置会被添加到web的响应header中 v2.9.6 1、将@Web的requireLogin默认值改为true,但是默认禁用,要配置sumk.http.login.enable=1才能启用 v2.9.7 1、StreamUtil改名为IOUtil 2、@Inject去掉beanClz()和handler() 3、接口文档增加一级属性的模糊搜索功能,比如name用_name=XX来搜索 4、微服务增加发送失败自动重发的机制 5、修改StartConstants和StartContext的包名 v2.9.8 1、增加@ExcludeInParams和@ExcludeInResponse,它用来标识dto中非参数字段或不是用于返回的字段。在web和rpc中都有效 2、允许pojo中存在跟@SoftDelete同名的字段,以达到复用软删除标识位的目的。这种情况下,往往只有一种状态是标识删除,其它状态都是非删除的 3、去掉sumk.db.default参数,增加s.alias.db.{name}参数 4、BeanPropertiesWatcher接口改名为BeanInjectWatcher v2.9.9 1、支持对配置中的redis密码进行加密 2、http接口支持名称相同,但是http方法不同的情况,并且支持接口的前缀匹配。对于前缀匹配的情况,WebUtil.getUrlLeft()可以获取剩下的url内容 3、SumkException的状态码全部改为负数,以跟BizException进行区分 4、将redis和数据库的初始化移到Plugin的prepare阶段,同时去掉Plugins类。修改之后,startAsyn阶段的数据库和redis都已经准备好了,不需要再用Plugins来判断 v2.9.10 1、对于常见的注解,使用相应Spec将它包装起来,以支持自定义注解。同时移除HttpActionNode的web()方法,upload()也改为返回UploadSpec v2.9.11 1、接口方式的rpc调用,如果返回值是泛型,不再需要手工调用JsonTypes进行注册 2、去掉@Param的custom属性,但是保留ParamSpec的custom属性,开发者可以通过这里的custom进行扩展 v2.9.12 1、添加BootWatcher接口,该接口允许在IOC启动前做一些操作 2、DB添加commit和rollback两个hook 3、增加对数据源的操作统计 v2.10.0 1、使用字节码等方式替换原有的http、rpc入参解析方式,减少反射引起动态生成class类 2、CalleeNode中的部分以arg开头的方法,改名为param开头,去掉isEmptyArgument方法,增加paramLength方法 3、微服务支持sumk.zkurl不配置(服务端和客户端都支持),这时候的rpc只能调用本进程里的微服务接口 4、DB钩子(hook)增加ON_COMMIT和CLOSED两种,并且入参从Runable改为Consumer 5、增加sumk.db.name.lowercase参数,用于将默认的表名和字段名改为小写,自定义名称不受本参数影响。这个原先需要通过注入自定义的实现类来实现 6、数据库selec类增加in方法,并且允许同一种比较出现两次,比如: name != '张三' and name != '李四' v2.10.1 1、Select增加notIn方法,用于sql中的not in() 2、简单的in查询、selectColumns指定查询列也都能使用缓存了 3、RecordRepository的包名改为org.yx.db.visit 4、除了默认的redis缓存外,还允许自定义缓存实现类 5、修改AbstractCachable的cacheEnable方法,这个只在自动生成的代码(sumk-codetool)里使用 6、@Bean增加辅助注解@ConditionOnPropert,支持组合关系:并且(用,或&&分隔)以及或者(用||分隔)。其中onMatch属性用来做取反操作 7、改进SumkDate转String的性能 v2.10.2 1、Select的offset、limit增加最大值1万的限制。可以通过代码突破这个限制。这意味着开发人员知道该语句比较耗性能 2、细节优化以提升性能或降低内存使用。比如用List或数组替代map,将一些计数器从Atomic类型改为普通类型等 3、提供监听器功能。监听器实现SumkListener接口,然后使用相应的EventBus发布事件。sumk-server-demo工程中InfoListener是它的使用例子 4、去掉DBEventListener接口,使用SumkListener改写数据库事件监听。sumk-server-demo工程中TableListener是它的使用例子 5、@Inject增加value属性,该属性强制指定注入bean的名称 6、去掉RedisUtil工具类(基本也用不到),增加Redis2接口 v3.0.0 1、Redis2接口改为Redis3x,并增加增加发布订阅方法。依赖的jedis从2.x升级到3.7.0 2、监听器增加异步功能,同时支持异步发布事件以及异步监听两种模式 3、配置文件的sumk.jetty开头的配置改为sumk.webserver开头 4、列表为基础的Map替代HashMap作为小Map使用,以压缩内存的的占用。从SBuilder.listMap()可以获取到 5、slf4j支持\\{}转义 6、除data外,其它http rest请求的paramter名称都在前面加_,比如sign改为_sign,这些参数不常用 7、重构rpc的序列化协议,并把mina改为netty。mina的支持移到sumk-rpc-mina项目中 8、升级了许多依赖包的版本,这些依赖包用原来的版本也是兼容的 与2.x不兼容的地方 1、两者的rpc协议不同,不能互相通信 2、http的sign、act、plainKey参数前面增加了_ 3、Redis2接口改为Redis3x v3.0.1 1、去掉Attachable接口,RpcUtil.attachments()改为RpcUtil v3.1.0 最主要的是将rpc透传数据的最大长度从255提升到60K,这个造成微服务跟旧版不兼容(无透传时仍然可以调用) 1、@Inject增加allowMulti属性,允许存在多个bean的情况下,取第一个注入 2、Select增加and方法,可以处理or表达式,也可以处理自定义的比较表达式 3、去掉Select的addEquals方法(此方法意义不大,增加理解成本),开发者通过多次调用addEqual来实现 4、优化DB代码,减少中间过程内存的使用量,并优化sql语句中的空格。不等号统一改为<> v3.1.1 1、UserSession的loadUserObject()改名为loadAndRefresh(),这个方法开发者一般也很少用到 2、增加Const.LISTENER_DB_MODIFY_ON_COMMIT类型监听器,它在事务提交前监听,能够操作数据库 3、ModifyEvent可以获取被影响条数,并且支持boolean类型的传参 v3.1.2 1、增加BeanProvider接口,IOC通过BeanProvider获取bean。方便与Spring等第三方IOC对接 v3.2.0 1、异常码从int类型改为String类型(int方式入参仍保留)。旧用户在客户端升级前,可通过sumk.http.interrorcode=1让返回值使用int方式 2、允许自定义数据库事务的实现 3、BizException的堆栈信息改为默认打印,可以通过sumk.bizexception.fullstack=0来关闭 v4.0.0 1、增加aop拦截器 2、将sumk工程拆分为base、framewrok、db、http、rpc五个工程,加上早就分出去的async-logger、sumk-rpc-mina,总共7个 v4.0.1 1、取消SumkException的code为负数的限制,并且将内置的负数改为正数 v4.0.2 1、新增对国际化的支持,主要类是I18n,并去掉工具类M。web和rpc对locale的解析和设置需要自己通过filter去实现 2、优化日志实现 v4.1.0 1、框架初始化改为多线程并行初始化,加快启动速度 2、支持自定义注册中心,内置注册中心还是zk 3、修改内置的分布式主键生成器,旧项目请在配置文件中添加sumk.seq.version=1 v4.2.0 1、添加sumk.factories,提供更加丰富的扩展支持 2、支持SumkServer.start(xx.class,xx)的方式扫描包路径,不需要显示配置sumk.ioc 3、zookeeper和jetty的依赖包版本升级 4、app.properties支持行尾\作为跨行连接符,这个跟Properties标准文件一致。它们最大的区别在于app.properties不支持转义字符 v4.2.1 1、slf4j支持2.x版本 ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # sumk sumk是为互联网而生的,在性能、分布式、扩展性等方面考虑较多,互联网常见的特性很多都内置支持,比如数据库读写分离、调用链跟踪、统一日志等,与spring体系相比,sumk更轻量、性能更高、内存消耗更低。以下是主要工程介绍,具体功能参见[sumk的功能特性](https://p2nwdvhb36.feishu.cn/docx/LQxXdjwbdoWDrFxcyUTcNWTUnSh) - sumk-base:配置管理和日志接口 - sumk-framework:核心框架,主要是IOC - sumk-db:数据库功能,包括事务、ORM等 - sumk-http:mvc组件 - Sumk-rpc:微服务功能 - sumk-rpc-mina:可选组件,如果引入该组件,rpc底层就会使用mina替代netty - async-logger(又名sumk-log):slf4j日志的实现,同时支持鹰眼跟踪和统一日志。 ### 功能介绍 [sumk总体介绍](https://p2nwdvhb36.feishu.cn/docx/AEIhdF4M5oDXouxdfNLc0ya2nZb) [sumk功能特性](https://p2nwdvhb36.feishu.cn/docx/LQxXdjwbdoWDrFxcyUTcNWTUnSh) ### 使用 [sumk框架入门](https://p2nwdvhb36.feishu.cn/docx/AOl0dhDqJoymnSxuWUhcTQ1SnMf) [注解及接口及工具类](https://p2nwdvhb36.feishu.cn/docx/UuIPduSDuo6kSlxSOEDcGzdPnSh) [sumk常用配置](https://p2nwdvhb36.feishu.cn/docx/RUBidOGQboZTkaxGJ8uc1Z93n8c) [sumk-db使用介绍](https://p2nwdvhb36.feishu.cn/docx/TQnUdmM1YomahpxVIKMcxPdYnFc) [接口文档及状态信息查看](https://p2nwdvhb36.feishu.cn/docx/ZvV3dCbLuog5wfxoSAgcV6frnvc) ### 示例工程 ### ### 作者:游夏。QQ:3205207767 ================================================ FILE: async-logger/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: async-logger/README.md ================================================ # sumk-log   async-logger是一款基于slf4j标准的日志系统。它的特点是高效、异步、支持配置中心统一配置,拥有鹰眼跟踪与统一日志等功能。它的前身名叫sumk-log ### 引入sumk-log.jar ```     com.github.youtongluan     async-logger     3.2.0 ``` 最新版本请查看maven中央库。 ### 配置说明(大多数配置都不需要重启系统): #### sumk.log.level=info,XX:debug,X.X:error 前面表示名称,后面是日志级别。如果没有名称,就表示是全局的。默认级别是INFO #### sumk.log.console=1 启用控制台输出 #### s.log.日志类型=path:日志存放路径;module:com.test,a.*;exclude:b.* * 日志类型现有day、month、level三种,支持扩展。统一日志的配置不在这里。以day日志为例,它可以简化为s.log.day=/logs/app-#.log * 日志存放路径中要有一个#,比如/log/daylog-#.log * module指定该日志所对应的日志名。支持头尾出现*作为通配符。这个配置是可选的 * exclude用于过滤掉不需要输出的日志。支持头尾出现*作为通配符。这个配置是可选的 * level类型日志跟day类型相似,差异是level日志除了module外,还增加了个level属性,它可以设置只输出摸个级别以上的日志 ### 统一日志 * 统一日志系统跟普通类型有点不同。它需要调用UnionLogs.start()来开启。 * 通过sumk.unionlog.module 配置统一日志允许打印的模块。 * 通过UnionLogs.getUnionLog().directOffer()方式输出的日志,不受module配置影响,无法关闭。这是因为统一日志需要具备审查的功能,不允许开发者随意关闭,所以采用代码和配置相结合的方式 * 统一日志默认也是输出到文件,可以自己定义输出位置。参见test目录下的UnionDemo ### 其它参数 * sumk.log.day.max day或level类型日志的日志文件保存个数,默认30 * sumk.log.month.max month类型的日志的日志文件保存个数,默认2 * 日志体达到某种大小后会被截短,这些参数的设置,参见[入门教程](https://github.com/youtongluan/sumk-server-demo)里的《sumk框架入门.docx》 #### 其它说明 * 将这个特性与统一配置相结合,就可以动态切换日志级别,也可以动态设置是否将某一类型日志发送到日志中心(统一日志)。 * sumk日志跟logback一个很大的不同在于,sumk日志的level是全局的,而logback的level是针对具体日志类型的。 ================================================ FILE: async-logger/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 async-logger com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots youtongluan 3205207767@qq.com https://www.oschina.net/p/sumk-log com.github.youtongluan sumk-base ${project.version} org.slf4j slf4j-api org.apache.logging.log4j log4j-to-slf4j 2.14.1 true junit junit test ================================================ FILE: async-logger/src/main/java/org/slf4j/impl/StaticLoggerBinder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.slf4j.impl; import org.slf4j.ILoggerFactory; import org.slf4j.spi.LoggerFactoryBinder; import org.yx.log.impl.SumkLoggerFactory; public class StaticLoggerBinder implements LoggerFactoryBinder { private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); public static final StaticLoggerBinder getSingleton() { return SINGLETON; } public static String REQUESTED_API_VERSION = "1.7.0"; private final ILoggerFactory loggerFactory; private StaticLoggerBinder() { loggerFactory = new SumkLoggerFactory(); } public ILoggerFactory getLoggerFactory() { return loggerFactory; } public String getLoggerFactoryClassStr() { return SumkLoggerFactory.class.getName(); } } ================================================ FILE: async-logger/src/main/java/org/slf4j/impl/StaticMDCBinder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.slf4j.impl; import org.slf4j.helpers.NOPMDCAdapter; import org.slf4j.spi.MDCAdapter; public class StaticMDCBinder { public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); private StaticMDCBinder() { } public MDCAdapter getMDCA() { return new NOPMDCAdapter(); } } ================================================ FILE: async-logger/src/main/java/org/slf4j/v2/SumkServiceProvider.java ================================================ package org.slf4j.v2; import org.slf4j.ILoggerFactory; import org.slf4j.IMarkerFactory; import org.slf4j.helpers.BasicMDCAdapter; import org.slf4j.helpers.BasicMarkerFactory; import org.slf4j.spi.MDCAdapter; import org.slf4j.spi.SLF4JServiceProvider; import org.yx.log.impl.SumkLoggerFactory; public class SumkServiceProvider implements SLF4JServiceProvider { private ILoggerFactory loggerFactory; private IMarkerFactory markerFactory; private MDCAdapter mdcAdapter; @Override public ILoggerFactory getLoggerFactory() { return loggerFactory; } @Override public IMarkerFactory getMarkerFactory() { return markerFactory; } @Override public MDCAdapter getMDCAdapter() { return mdcAdapter; } @Override public String getRequestedApiVersion() { return "2.0.1"; } @Override public void initialize() { this.loggerFactory = new SumkLoggerFactory(); this.markerFactory = new BasicMarkerFactory(); this.mdcAdapter = new BasicMDCAdapter(); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/CodeLine.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; public class CodeLine { final String className; final String methodName; final int lineNumber; public CodeLine(String className, String methodName, int lineNumber) { this.className = className; this.methodName = methodName; this.lineNumber = lineNumber; } public String getClassName() { return className; } public String getMethodName() { return methodName; } public int getLineNumber() { return lineNumber; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/CodeLineKit.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Objects; import java.util.function.BiFunction; import org.slf4j.Marker; import org.yx.log.CodeLineMarker; public final class CodeLineKit { private static BiFunction parser = (marker, logModule) -> { if (marker != null && marker.getClass() == CodeLineMarker.class) { return LogHelper.extractCodeLine(marker.getName()); } return LogHelper.extractCodeLine("org.yx.log."); }; public static BiFunction getParser() { return parser; } public static void setParser(BiFunction parser) { CodeLineKit.parser = Objects.requireNonNull(parser); } public static CodeLine parse(Marker marker, String name) { return parser.apply(marker, name); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/DayRollingFileAppender.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.time.LocalDate; import java.util.Objects; import org.yx.conf.AppInfo; import org.yx.util.SumkDate; public class DayRollingFileAppender extends RollingFileAppender { public DayRollingFileAppender() { super("day"); } public DayRollingFileAppender(String name) { super(Objects.requireNonNull(name)); } @Override protected boolean shouldDelete(String fileName) { String c = LogHelper.realContext(fileName, filePattern, SLOT); if (c == null || c.length() != 10) { return false; } try { LocalDate logDate = LocalDate.parse(c); LocalDate now = LocalDate.now(); return logDate.isBefore(now.minusDays(AppInfo.getInt("sumk.log.day.max", 30))); } catch (Exception e) { return false; } } @Override protected String formatDateString(SumkDate date) { return date.to_yyyy_MM_dd(); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/DefaultUnionLog.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import org.yx.base.matcher.Matchers; import org.yx.conf.AppInfo; import org.yx.conf.SystemConfig; public class DefaultUnionLog extends LogQueue implements UnionLog { private UnionLogDao dao; private boolean started; private Consumer observer; private Function logObjectSerializer; private Supplier> matcherSupplier; public DefaultUnionLog() { super("unionlog"); this.dao = new LocalFileDao(); this.matcherSupplier = () -> Matchers.includeAndExclude(AppInfo.get("sumk.unionlog.module", null), AppInfo.get("sumk.unionlog.exclude", null)); } @Override protected void flush(boolean idle) throws Exception { this.dao.flush(idle); } @Override protected void output(List list) throws Exception { List logs = new ArrayList<>(list.size()); for (LogObject raw : list) { UnionLogObject log = this.logObjectSerializer.apply(raw); if (log == null) { continue; } logs.add(log); } if (logs.size() > 0) { this.dao.store(logs); } } @Override protected boolean onStart(Map configMap) { if (this.logObjectSerializer == null) { this.logObjectSerializer = getLogObjectSerializer(); } if (this.observer == null) { this.observer = c -> setMatcher(matcherSupplier.get()); AppInfo.addObserver(this.observer); } this.started = true; return true; } @Override public synchronized void stop() throws Exception { this.started = false; super.stop(); } @Override public boolean directOffer(LogObject logObject) { if (!this.started) { return false; } return this.queue.offer(logObject); } @Override public boolean offer(LogObject logObject) { if (!this.started) { return false; } return super.offer(logObject); } @Override public boolean isStarted() { return this.started; } public Supplier> getMatcherSupplier() { return matcherSupplier; } public void setMatcherSupplier(Supplier> matcherSupplier) { this.matcherSupplier = Objects.requireNonNull(matcherSupplier); this.setMatcher(this.matcherSupplier.get()); } public Function getLogObjectSerializer() { return logObjectSerializer == null ? new UnionLogObjectSerializer() : this.logObjectSerializer; } public void setLogObjectSerializer(Function logObjectSerializer) { this.logObjectSerializer = Objects.requireNonNull(logObjectSerializer); } public UnionLogDao getDao() { return dao; } public void setDao(UnionLogDao dao) { this.dao = Objects.requireNonNull(dao); } public void removeObserver() { Consumer ob = this.observer; if (ob == null) { return; } AppInfo.removeObserver(ob); this.observer = null; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LeveledDayRollingFileAppender.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Collections; import java.util.Map; import org.yx.log.ConsoleLog; import org.yx.log.LogLevel; public class LeveledDayRollingFileAppender extends DayRollingFileAppender { private LogLevel level = LogLevel.INFO; public LeveledDayRollingFileAppender() { this("level"); } public LeveledDayRollingFileAppender(String name) { super(name); } @Override protected boolean accept(LogObject logObject) { return logObject.methodLevel.ordinal() >= level.ordinal() && super.accept(logObject); } @Override public void config(Map configMap) { if (configMap == null) { configMap = Collections.emptyMap(); } super.config(configMap); String lev = configMap.get("level"); if (lev == null || lev.isEmpty()) { return; } try { this.level = LogLevel.valueOf(lev.toUpperCase()); } catch (Exception e) { ConsoleLog.defaultLog.error("{} is not a valid level name", lev); } } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LocalFileDao.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; import org.yx.conf.AppInfo; import org.yx.log.ConsoleLog; import org.yx.util.UUIDSeed; public class LocalFileDao implements UnionLogDao { private static final String LINE_SPLIT = "\n"; private final long MAX_FILE_LENGTH = AppInfo.getInt("sumk.log.union.max_file_length", 100 * 1024 * 1024); private final int MAX_RECORD_SIZE = AppInfo.getInt("sumk.log.union.max_record_size", 200); private int aliveTime = AppInfo.getInt("sumk.log.union.alive_time", 15000); private long createTime = -1; private int fileLength; private int recordSize; private List buffer; public LocalFileDao() { this.buffer = new ArrayList<>(MAX_RECORD_SIZE); } private File createLogingFile() { for (int i = 0; i < 5; i++) { try { String name = AppInfo.pid().concat("_").concat(UUIDSeed.seq18()); File f = new File(getLogingPath(), name); if (!f.getParentFile().exists()) { File parent = f.getParentFile(); if (!parent.mkdirs()) { ConsoleLog.defaultLog.error("create folder " + parent.getAbsolutePath() + " failed!!!"); return null; } } if (!f.createNewFile()) { ConsoleLog.defaultLog.error("create file " + f.getAbsolutePath() + " failed!!!"); return null; } return f; } catch (Exception e) { ConsoleLog.defaultLog.error(e.getMessage(), e); } } return null; } @Override public void store(List logs) throws IOException { int logCount = logs.size(); StringBuilder sb = new StringBuilder(600 * Math.min(10, logs.size())); for (UnionLogObject log : logs) { sb.append(log.log).append(LINE_SPLIT); } byte[] bs = sb.toString().getBytes(AppInfo.UTF8); sb = null; buffer.add(bs); this.recordSize += logCount; this.fileLength += bs.length; if (this.recordSize >= MAX_RECORD_SIZE || this.fileLength > MAX_FILE_LENGTH) { reset(); } } @Override public void flush(boolean idle) { if (this.recordSize >= MAX_RECORD_SIZE || this.fileLength > MAX_FILE_LENGTH || System.currentTimeMillis() - createTime >= aliveTime) { reset(); } } public void reset() { List datas = this.buffer; if (datas.isEmpty()) { return; } this.buffer = new ArrayList<>(); long size = 0; FileChannel channel = null; try { File logFile = createLogingFile(); channel = FileChannel.open(logFile.toPath(), StandardOpenOption.APPEND); ByteBuffer[] bufs = new ByteBuffer[datas.size()]; for (int i = 0; i < bufs.length; i++) { bufs[i] = ByteBuffer.wrap(datas.get(i)); size += datas.get(i).length; } do { size -= channel.write(bufs); } while (size != 0); channel.force(true); channel.close(); channel = null; move2Loged(logFile); this.fileLength = 0; this.recordSize = 0; } catch (Exception e) { ConsoleLog.defaultLog.error(e.toString(), e); } finally { if (channel != null) { try { channel.close(); } catch (IOException e) { ConsoleLog.defaultLog.error(e.toString(), e); } } } } protected void move2Loged(File logFile) { UnionLogUtil.move2Loged(logFile); } protected File getLogingPath() { return UnionLogUtil.getLogingPath(); } public void setAliveTime(int time) { this.aliveTime = time; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogAppendObserver.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.function.Consumer; import org.yx.conf.AppInfo; import org.yx.conf.SystemConfig; import org.yx.log.ConsoleLog; import org.yx.log.LogSettings; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class LogAppendObserver implements Consumer { @Override public void accept(SystemConfig info) { LogSettings.updateSettings(); LogObject.updateCodeLineOnOff(); Map newAppenders = AppInfo.subMap(LogAppenders.LOG_APPENDER); for (LogAppender append : LogAppenders.logAppenders) { String v = newAppenders.remove(append.name()); if (v == null || v.isEmpty()) { try { append.stop(); } catch (Exception e) { ConsoleLog.defaultLog.error(e.toString(), e); } continue; } v = StringUtil.toLatin(v); Map map = CollectionUtil.loadMapFromText(v, ";", ":"); append.config(map); } if (newAppenders.isEmpty()) { return; } List appends = new ArrayList<>(); for (LogAppender append : LogAppenders.logAppenders) { appends.add(append); } if (LogAppenders.isStarted()) { ConsoleLog.defaultLog.info("find new appends:{}", newAppenders); } for (Entry entry : newAppenders.entrySet()) { String k = entry.getKey(); String p = entry.getValue(); LogAppender appender = LogAppenders.startAppender(k, p); if (appender != null) { appends.add(appender); } } LogAppenders.logAppenders = appends.toArray(new LogAppender[appends.size()]); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogAppender.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Map; public interface LogAppender { boolean start(Map configMap); void stop() throws Exception; boolean offer(LogObject logObject); String name(); void config(Map configMap); } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogAppenderFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.yx.log.ConsoleLog; public final class LogAppenderFactory { private static final ConcurrentMap map = new ConcurrentHashMap<>(); static synchronized void init() throws Exception { Collection appenders = Arrays.asList(new LeveledDayRollingFileAppender(), new MonthRollingFileAppender(), new DayRollingFileAppender()); for (LogAppender append : appenders) { map.put(append.name(), append); } } static LogAppender start(String name, Map configMap) throws Exception { LogAppender append = map.get(name); if (append == null) { ConsoleLog.defaultLog.error("{} cannot find appender", name); return null; } if (!append.start(configMap)) { ConsoleLog.defaultLog.error("{} started failed,value is {}", name, configMap); return null; } return append; } public static LogAppender registeAppender(LogAppender m) { Objects.requireNonNull(m); return map.put(m.name(), m); } public static void remove(String name) { if (name == null || name.isEmpty()) { return; } map.remove(name); } public static LogAppender getAppender(String name) { return map.get(name); } public static Set appenderNames() { return Collections.unmodifiableSet(map.keySet()); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogAppenders.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import org.yx.conf.AppInfo; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class LogAppenders { static final String LOG_APPENDER = "s.log."; public static final String MODULE = "module"; public static final String PATH = "path"; static LogAppender[] logAppenders = new LogAppender[0]; private static boolean started; static LogAppender startAppender(String name, String value) { if (value == null || value.isEmpty()) { return null; } value = StringUtil.toLatin(value); try { return LogAppenderFactory.start(name, CollectionUtil.loadMapFromText(value, ";", ":")); } catch (Throwable e) { System.err.println("appender [" + name + "] = " + value + " create failed"); e.printStackTrace(); return null; } } static synchronized void init() { if (started) { return; } started = true; try { LogAppenderFactory.init(); AppInfo.addObserver(new LogAppendObserver()); } catch (Exception e) { e.printStackTrace(); } } public static boolean offer(LogObject logObject) { boolean output = false; for (LogAppender log : logAppenders) { if (log.offer(logObject)) { output = true; } } if (UnionLogs.getUnionLog().offer(logObject)) { return true; } return output; } public static boolean isStarted() { return started; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogHelper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Objects; import org.yx.log.ConsoleLog; public class LogHelper { private static PlainOutput output = new PlainOutputImpl(); public static void setOutput(PlainOutput output) { LogHelper.output = Objects.requireNonNull(output); } public static String plainMessage(LogObject logObject, boolean showAttachs) { return output.plainMessage(logObject, showAttachs); } public static void plainMessage(StringBuilder sb, LogObject logObject, boolean showAttachs) { output.plainMessage(sb, logObject, showAttachs); } public static CodeLine extractCodeLine(String pre) { if (pre == null || pre.isEmpty()) { return null; } StackTraceElement stack[] = (new Throwable()).getStackTrace(); if (stack != null && stack.length > 2) { int i = stack.length - 1; for (; i > 0; i--) { if (stack[i].getClassName().startsWith(pre)) { i++; break; } } StackTraceElement s = stack[i]; if (s.getClassName().contains(".sumkbox.") && (++i) < stack.length) { s = stack[i]; } return new CodeLine(s.getClassName(), s.getMethodName(), s.getLineNumber()); } return null; } public static String realContext(String text, String pattern, String slot) { String[] fs = pattern.split(slot, 2); if (fs.length != 2) { ConsoleLog.defaultLog.error("{} should contain and only contain one {}", pattern, slot); return null; } if (fs[0].length() + fs[1].length() > text.length()) { return null; } int end = text.length() - fs[1].length(); if (!fs[0].equals(text.substring(0, fs[0].length())) || !fs[1].equals(text.substring(end))) { return null; } return text.substring(fs[0].length(), end); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogObject.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map; import org.slf4j.Marker; import org.yx.annotation.doc.NotNull; import org.yx.base.context.ActionContext; import org.yx.base.context.LogContext; import org.yx.conf.AppInfo; import org.yx.log.LogKits; import org.yx.log.LogLevel; import org.yx.log.LogSettings; import org.yx.log.SumkLogger; import org.yx.util.SumkDate; public class LogObject { public static final char LN = '\n'; public static final Charset CHARSET = StandardCharsets.UTF_8; private static boolean codelineEnable = true; @NotNull final SumkDate logDate; @NotNull final LogLevel methodLevel; final String body; @NotNull final String threadName; final Throwable exception; @NotNull final String loggerName; final CodeLine codeLine; final LogContext logContext; public static LogObject create(Marker marker, LogLevel methodLevel, String message, Throwable e, SumkLogger logger) { String loggerName = logger.getName(); CodeLine codeLine = null; if (codelineEnable && (!loggerName.startsWith("sumk.") || marker != null)) { codeLine = CodeLineKit.parse(marker, loggerName); } return new LogObject(loggerName, SumkDate.now(), methodLevel, LogKits.shorterSubfix(message, LogSettings.maxBodyLength()), e, Thread.currentThread().getName(), ActionContext.current().logContext(), codeLine); } public LogObject(@NotNull String loggerName, @NotNull SumkDate logDate, @NotNull LogLevel methodLevel, String body, Throwable exception, @NotNull String threadName, LogContext logContext, CodeLine codeLine) { this.logDate = logDate; this.methodLevel = methodLevel; this.body = body; this.exception = exception; this.threadName = threadName; this.logContext = logContext != null ? logContext : LogContext.empty(); this.loggerName = loggerName; this.codeLine = codeLine; } public String spanId() { return logContext.spanId; } public String traceId() { return logContext.traceId; } public String userId() { return logContext.userId; } public boolean isTest() { return logContext.test; } public Map attachments() { return logContext.unmodifiedAttachs(); } public SumkDate getLogDate() { return logDate; } public LogLevel getMethodLevel() { return methodLevel; } public String getBody() { return body; } public String getThreadName() { return threadName; } public Throwable getException() { return exception; } public String getLoggerName() { return this.loggerName; } public CodeLine getCodeLine() { return codeLine; } public LogContext getLogContext() { return logContext; } static void updateCodeLineOnOff() { codelineEnable = AppInfo.getBoolean("sumk.log.codeline", true); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/LogQueue.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.conf.AppInfo; import org.yx.log.ConsoleLog; import org.yx.util.SumkThreadPool; public abstract class LogQueue implements Runnable { protected final String name; private int interval; private long handleLogCount; private boolean jobStarted; protected final BlockingQueue queue; private Predicate matcher = BooleanMatcher.FALSE; public LogQueue(String name) { this.name = name; this.interval = AppInfo.getInt("sumk.log.interval." + name, 1000); this.queue = new LinkedBlockingQueue<>(Integer.getInteger("sumk.log.queue." + name, 2_0000)); } protected boolean accept(LogObject logObject) { String module = logObject.getLoggerName(); return this.matcher.test(module); } protected abstract void flush(boolean idle) throws Exception; public void config(Map configMap) { if (configMap == null) { configMap = Collections.emptyMap(); } String patterns = configMap.get(LogAppenders.MODULE); if (patterns == null || patterns.isEmpty()) { patterns = Matchers.WILDCARD; } this.matcher = Matchers.includeAndExclude(patterns, configMap.get("exclude")); ConsoleLog.defaultLog.debug("{} set matcher :{}", this.name, this.matcher); } public final String name() { return this.name; } public boolean offer(LogObject logObject) { if (!accept(logObject)) { return false; } return queue.offer(logObject); } protected abstract void output(List list) throws Exception; @Override public void run() { Thread.currentThread().setName("log-" + this.name); while (true) { try { this.flush(this.consume()); } catch (Throwable e) { ConsoleLog.defaultLog.warn("日志消费失败," + e.toString(), e); if (Thread.currentThread().isInterrupted() || e.getClass() == InterruptedException.class) { ConsoleLog.defaultLog.warn("{}日志停止了", this.name); Thread.currentThread().interrupt(); return; } } } } private boolean consume() throws Exception { LogObject message = queue.poll(interval, TimeUnit.MILLISECONDS); if (message == null) { return true; } int batch = Math.min(queue.size() + 10, 100); List list = new ArrayList<>(batch); list.add(message); queue.drainTo(list, batch - 1); while (list.size() > 0) { output(list); this.handleLogCount += list.size(); list.clear(); queue.drainTo(list, batch); } return false; } public synchronized boolean start(Map map) { if (map == null) { map = Collections.emptyMap(); } if (!onStart(map)) { return false; } ConsoleLog.defaultLog.debug("{} started by {}", this, map); if (!jobStarted) { startJob(); this.jobStarted = true; } return jobStarted; } protected void startJob() { SumkThreadPool.executor().execute(this); } protected abstract boolean onStart(Map configMap); public synchronized void stop() throws Exception { this.matcher = BooleanMatcher.FALSE; ConsoleLog.defaultLog.info("日志{} stoped", this.name); } public void setInterval(int interval) { if (interval > 0) { this.interval = interval; } } public long getHandleLogCount() { return handleLogCount; } protected Predicate getMatcher() { return matcher; } protected void setMatcher(Predicate matcher) { this.matcher = Objects.requireNonNull(matcher); } @Override public String toString() { return this.name + " [queue size:" + queue.size() + ",matcher:" + matcher + ",logCount:" + handleLogCount + "]"; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/MonthRollingFileAppender.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.time.LocalDate; import java.util.Objects; import org.yx.conf.AppInfo; import org.yx.util.SumkDate; public class MonthRollingFileAppender extends RollingFileAppender { public MonthRollingFileAppender() { super("month"); } public MonthRollingFileAppender(String name) { super(Objects.requireNonNull(name)); } @Override protected String formatDateString(SumkDate date) { return date.to_yyyy_MM(); } @Override protected boolean shouldDelete(String fileName) { String c = LogHelper.realContext(fileName, filePattern, SLOT); if (c == null || c.length() != 7) { return false; } c += "-01"; try { LocalDate logDate = LocalDate.parse(c); LocalDate now = LocalDate.now().withDayOfMonth(1); return logDate.isBefore(now.minusMonths(AppInfo.getInt("sumk.log.month.max", 2))); } catch (Exception e) { return false; } } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/PlainOutput.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; public interface PlainOutput { String plainMessage(LogObject logObject, boolean showAttachs); void plainMessage(StringBuilder sb, LogObject logObject, boolean showAttachs); void setShowSN(boolean showSN); void setShowThreadName(boolean showThreadName); } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/PlainOutputImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Map; import java.util.Objects; import org.yx.base.context.ActionContext; import org.yx.log.LogKits; import org.yx.log.LogSettings; import org.yx.util.ExceptionUtil; public class PlainOutputImpl implements PlainOutput { private boolean showThreadName = true; private boolean showSN = true; @Override public void setShowThreadName(boolean showThreadName) { this.showThreadName = showThreadName; } @Override public void setShowSN(boolean showSN) { this.showSN = showSN; } public void plainMessage(StringBuilder sb, LogObject logObject, boolean showAttachs) { this.appendHeader(sb, logObject).append(logObject.methodLevel).append(' '); if (logObject.codeLine != null) { String clzShortName = LogKits.shorterPrefix(logObject.codeLine.className, LogSettings.maxLogNameLength()); if (!Objects.equals(logObject.getLoggerName(), logObject.codeLine.className)) { sb.append('(').append(logObject.getLoggerName()).append(')'); } sb.append(clzShortName).append(':').append(logObject.codeLine.lineNumber); } else { sb.append(logObject.getLoggerName()); } Map attachs = logObject.attachments(); if (showAttachs && attachs != null) { sb.append(" #").append(attachs); } sb.append(" - ").append(logObject.body).append(LogObject.LN); if (logObject.exception != null) { ExceptionUtil.printStackTrace(sb, logObject.exception); sb.append(LogObject.LN); } } protected StringBuilder appendHeader(StringBuilder sb, LogObject logObject) { StringBuilder sn = new StringBuilder(); if (logObject.userId() != null) { sn.append(logObject.userId()); } if (logObject.traceId() != null) { if (sn.length() > 0) { sn.append('@'); } sn.append(logObject.traceId()); if (logObject.spanId() != null) { sn.append('-').append(logObject.spanId()); } } sb.append(logObject.logDate.to_yyyy_MM_dd_HH_mm_ss_SSS()); if (ActionContext.current().isTest()) { sb.append(" -TEST- "); } if (showThreadName) { sb.append(" [").append(logObject.threadName).append("] "); } if (showSN && sn.length() > 0) { sb.append(" {").append(sn).append("} "); } return sb; } @Override public String plainMessage(LogObject logObject, boolean showAttachs) { StringBuilder sb = new StringBuilder(64); this.plainMessage(sb, logObject, showAttachs); return sb.toString(); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/RollingFileAppender.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.Map; import org.yx.base.matcher.BooleanMatcher; import org.yx.conf.AppInfo; import org.yx.log.ConsoleLog; import org.yx.log.LogSettings; import org.yx.util.StringUtil; import org.yx.util.SumkDate; public abstract class RollingFileAppender extends LogQueue implements LogAppender { public static final String SLOT = "#"; private static final long DAY_MILS = 1000L * 3600 * 24; protected StringBuilder buffer; protected String currentDate; protected FileChannel channel; private boolean dirty; private int bufferSize = AppInfo.getInt("sumk.log.buffer.size", 2048); protected int syncInterval = AppInfo.getInt("sumk.log.sync.interval", 3000); private long lastDeleteTime; private long lastSyncTime; protected String filePattern; protected File dir; protected boolean setupFilePath(String fileName) { if (StringUtil.isEmpty(fileName)) { return false; } if (fileName.indexOf(SLOT) < 0) { ConsoleLog.defaultLog.error("{} should contain {}", fileName, SLOT); return false; } if (fileName.indexOf(SLOT) != fileName.lastIndexOf(SLOT)) { ConsoleLog.defaultLog.error("{} contain more than one {}", fileName, SLOT); return false; } File file = new File(fileName); this.filePattern = file.getName(); if (!this.filePattern.contains(SLOT)) { ConsoleLog.defaultLog.error("{} should contain {}", this.filePattern, SLOT); return false; } this.dir = file.getParentFile(); if (!this.dir.exists() && !this.dir.mkdirs()) { ConsoleLog.defaultLog.error("directory [{}{}] is not exists, and cannot create!!!", this.dir.getAbsolutePath(), File.pathSeparator); return false; } return true; } public RollingFileAppender(String name) { super(name); } @Override protected void flush(boolean idle) { if (this.buffer != null && this.buffer.length() > bufferSize) { this.buffer = null; } if (this.getMatcher() == BooleanMatcher.FALSE && this.channel != null) { this.sync(); this.closeCurrentChannel(); } long now = System.currentTimeMillis(); if (this.dirty && (now - this.lastSyncTime >= this.syncInterval)) { this.lastSyncTime = now; this.sync(); } if (now - this.lastDeleteTime >= DAY_MILS) { this.lastDeleteTime = now; this.deleteHisLog(); } } protected void deleteHisLog() { String[] files = dir.list(); if (files == null || files.length == 0) { return; } for (String f : files) { if (this.shouldDelete(f)) { try { File log = new File(dir, f); log.delete(); } catch (Exception e) { } } } } @Override protected void output(List msgs) throws IOException { String date = this.formatDateString(msgs.get(0).logDate); int fromIndex = 0; for (int i = 1; i < msgs.size(); i++) { LogObject obj = msgs.get(i); String d2 = this.formatDateString(obj.logDate); if (!d2.equals(date)) { this.outputInSameDate(date, msgs.subList(fromIndex, i)); date = d2; fromIndex = i; } } if (fromIndex == 0) { this.outputInSameDate(date, msgs); return; } this.outputInSameDate(date, msgs.subList(fromIndex, msgs.size())); } protected void outputInSameDate(String date, List msgs) throws IOException { if (!date.equals(this.currentDate)) { if (this.channel != null) { this.sync(); this.closeCurrentChannel(); } File file = new File(this.dir, filePattern.replace(SLOT, date)); if (!file.exists() && !file.createNewFile()) { ConsoleLog.defaultLog.error("{} create fail ", file.getAbsolutePath()); for (LogObject logObject : msgs) { System.err.print(LogHelper.plainMessage(logObject, LogSettings.showAttach())); } return; } this.channel = FileChannel.open(file.toPath(), StandardOpenOption.APPEND); this.currentDate = date; } long size = 0; ByteBuffer[] bufs = new ByteBuffer[msgs.size()]; for (int i = 0; i < bufs.length; i++) { byte[] b = toBytes(msgs.get(i)); bufs[i] = ByteBuffer.wrap(b); size += b.length; } do { size -= this.channel.write(bufs); } while (size != 0); this.dirty = true; } protected void sync() { try { this.channel.force(true); this.dirty = false; ConsoleLog.defaultLog.trace("{} finish sync to {}", this.name, this.currentDate); } catch (Exception e) { ConsoleLog.defaultLog.error(this.name + "刷新到磁盘失败[" + this.currentDate + "]", e); } } protected void closeCurrentChannel() { try { this.channel.close(); this.channel = null; this.currentDate = null; ConsoleLog.defaultLog.debug("{} closed {}", this.name, this.currentDate); } catch (Exception e) { ConsoleLog.defaultLog.error(this.name + "关闭失败[" + this.currentDate + "]", e); } } protected abstract boolean shouldDelete(String fileName); protected byte[] toBytes(LogObject logObject) { if (this.buffer == null) { this.buffer = new StringBuilder(bufferSize); } else { this.buffer.setLength(0); } LogHelper.plainMessage(this.buffer, logObject, LogSettings.showAttach()); return this.buffer.toString().getBytes(LogObject.CHARSET); } protected abstract String formatDateString(SumkDate date); protected String extractPath(Map map) { String path = map.get(LogAppenders.PATH); if (path != null || map.size() != 1) { return path; } String p = map.keySet().iterator().next(); String v = map.get(p); return StringUtil.isEmpty(v) ? p : String.join(":", p, v); } @Override protected boolean onStart(Map map) { this.config(map); return this.dir != null && this.filePattern != null; } @Override public void config(Map map) { String path = extractPath(map); if (!setupFilePath(path)) { return; } super.config(map); } public File getLogFile(SumkDate date) { String name = filePattern.replace(SLOT, formatDateString(date)); return new File(dir, name); } public void setSyncInterval(int syncInterval) { this.syncInterval = syncInterval; } public int getBufferSize() { return bufferSize; } public void setBufferSize(int bufferSize) { this.bufferSize = Math.max(bufferSize, 100); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/SumkLoggerFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Objects; import java.util.function.Function; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.yx.log.LogLevel; import org.yx.log.Loggers; import org.yx.log.RawLog; import org.yx.log.SumkLogger; public final class SumkLoggerFactory implements ILoggerFactory { private static final Loggers loggers = Loggers.create("Slf4jLog"); private static Function loggerFactory = SumkLoggerImpl::new; static { LogAppenders.init(); RawLog.setLogger(RawLog.SLF4J_LOG); } public SumkLoggerFactory() { RawLog.info("org.slf4j.sumk", "sumk slf4j initialized"); } public static Function getLoggerFactory() { return loggerFactory; } public static void setLoggerFactory(Function loggerFactory) { SumkLoggerFactory.loggerFactory = Objects.requireNonNull(loggerFactory); } public static void setDefaultLevel(LogLevel level) { if (level != null) { Loggers.setDefaultLevel(level); } } @Override public Logger getLogger(String name) { SumkLogger log = loggers.get(name); if (log != null) { return log; } log = loggerFactory.apply(name); SumkLogger oldInstance = loggers.putIfAbsent(name, log); return oldInstance == null ? log : oldInstance; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/SumkLoggerImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import org.slf4j.Marker; import org.slf4j.event.LoggingEvent; import org.slf4j.spi.LocationAwareLogger; import org.yx.base.context.LogContext; import org.yx.log.CodeLineMarker; import org.yx.log.ConsoleLog; import org.yx.log.LogKits; import org.yx.log.LogLevel; import org.yx.log.LogSettings; import org.yx.log.SumkLogger; import org.yx.util.SumkDate; public class SumkLoggerImpl extends SumkLogger implements LocationAwareLogger { public SumkLoggerImpl(String module) { super(module); } @Override protected void output(Marker marker, LogLevel methodLevel, String format, Object... arguments) { try { String msg = LogKits.buildMessage(format, arguments); LogObject logObject = LogObject.create(marker, methodLevel, msg, null, this); if (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) { System.out.print(LogHelper.plainMessage(logObject, LogSettings.showAttach())); } } catch (Exception e) { e.printStackTrace(); } } @Override protected void output(Marker marker, LogLevel methodLevel, String msg, Throwable e) { try { LogObject logObject = LogObject.create(marker, methodLevel, msg, e, this); if (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) { System.err.print(LogHelper.plainMessage(logObject, LogSettings.showAttach())); } } catch (Exception e2) { e2.printStackTrace(); } } @Override public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t) { try { LogLevel methodLevel = LogKits.fromSlf4jLocationAwareLoggerInt(level); if (!this.isLogable(methodLevel)) { return; } String msg = LogKits.buildMessage(message, argArray); if (fqcn != null && !fqcn.equals(name)) { marker = new CodeLineMarker(fqcn); } LogObject logObject = LogObject.create(marker, methodLevel, msg, t, this); if (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) { System.out.print(LogHelper.plainMessage(logObject, LogSettings.showAttach())); } } catch (Exception e) { ConsoleLog.defaultLog.error("failed when append to log queue", e); } } public void log(LoggingEvent event) { LogLevel methodLevel = LogKits.fromSlf4jLocationAwareLoggerInt(event.getLevel().toInt()); if (!this.isLogable(methodLevel)) { return; } try { String msg = "*** " + LogKits.buildMessage(event.getMessage(), event.getArgumentArray()); LogObject logObject = new LogObject(name, SumkDate.of(event.getTimeStamp()), methodLevel, msg, event.getThrowable(), event.getThreadName(), LogContext.empty(), null); if (!LogAppenders.offer(logObject) || LogSettings.consoleEnable()) { System.out.print(LogHelper.plainMessage(logObject, LogSettings.showAttach())); } } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/UnionLog.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Map; public interface UnionLog { boolean start(Map configMap); boolean isStarted(); void stop() throws Exception; boolean offer(LogObject logObject); boolean directOffer(LogObject logObject); } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/UnionLogDao.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.List; public interface UnionLogDao { /** * 一段时间触发,调用间隔一般不超过interval,但有可能接近0。有无日志都会调用 * * @param idle true表示本次任务没有日志 */ void flush(boolean idle); /** * 输出日志对象,有日志的时候都会调用 * * @param logs 日志对象列表,不为null,也不为空。里面的元素也都不为null * @throws Exception 如果抛出异常,会导致本次要处理的日志丢失,不影响之后日志的消费 */ void store(List logs) throws Exception; } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/UnionLogObject.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import org.yx.annotation.doc.NotNull; import org.yx.util.SumkDate; /** * 调用toLog()方法,可以获取到json化后的日志体。
* 如果要减少中间态String的生成数量,可以调用getLogContext()获取CharSequence类型的日志体。 */ public class UnionLogObject { @NotNull protected final String name; @NotNull protected final SumkDate date; @NotNull protected final String log; public UnionLogObject(@NotNull String name, @NotNull SumkDate date, @NotNull String log) { this.name = name; this.date = date; this.log = log; } /** * @return 日志名称 */ public String getName() { return name; } /** * @return 日志打印时的时间 */ public SumkDate getDate() { return date; } /** * 一般是json结构,并且里面字段的顺序一般也是固定的。 默认第一个前2个是name和date * * @return 格式化后的日志 */ public String getLog() { return this.log; } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/UnionLogObjectSerializer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.io.IOException; import java.util.function.Function; import org.yx.conf.AppInfo; import org.yx.log.ConsoleLog; public class UnionLogObjectSerializer implements Function { private final String appId; private int extraSize = 350; public UnionLogObjectSerializer() { this.appId = AppInfo.appId(null); } @Override public UnionLogObject apply(LogObject log) { int estimate = extraSize; if (log.body != null) { estimate += log.body.length(); } StringBuilder sb = new StringBuilder(estimate); try { UnionLogUtil.appendLogObject(sb, log, appId); return new UnionLogObject(log.loggerName, log.logDate, sb.toString()); } catch (IOException e) { ConsoleLog.defaultLog.error("数据解析出错", e); return null; } } public int getExtraSize() { return extraSize; } public void setExtraSize(int extraSize) { if (extraSize > 0) { this.extraSize = extraSize; } } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/UnionLogUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import org.yx.base.sumk.UnsafeStringWriter; import org.yx.conf.AppInfo; import org.yx.exception.CodeException; import org.yx.log.ConsoleLog; import org.yx.util.ExceptionUtil; import com.google.gson.stream.JsonWriter; public final class UnionLogUtil { private static File logRoot; private static String LOGING = System.getProperty("sumk.log.union.login", "loging"); private static String LOGED = System.getProperty("sumk.log.union.loged", "loged"); private static String ERROR = System.getProperty("sumk.log.union.error", "error"); public static void move2Loged(File logging) { if (logging == null) { return; } File dest = new File(getLogedPath(), logging.getName()); File p = dest.getParentFile(); if (!p.exists()) { p.mkdirs(); } if (!logging.renameTo(dest)) { ConsoleLog.defaultLog.error(logging.getName() + " move to loged folder failed"); } } public static void move2Error(File loged) { if (loged == null) { return; } File dest = new File(getErrorPath(), loged.getName()); File p = dest.getParentFile(); if (!p.exists()) { p.mkdirs(); } if (!loged.renameTo(dest)) { ConsoleLog.defaultLog.error(loged.getName() + " move to error path failed"); } } public static File getErrorPath() { return new File(getLogRoot(), ERROR); } public static File getLogedPath() { return new File(getLogRoot(), LOGED); } public static File getLogingPath() { return new File(getLogRoot(), LOGING); } private static File getLogRoot() { if (logRoot != null) { return logRoot; } logRoot = getDefaultLoginPath(); ConsoleLog.defaultLog.info("logRoot:" + logRoot.getAbsolutePath()); return logRoot; } private static File getDefaultLoginPath() { String path = System.getProperty("sumk.log.union.path"); if (path != null && path.length() > 2) { return new File(path); } if (System.getProperty("os.name").toLowerCase().startsWith("win")) { File f = new File("D:"); if (f.exists() && f.getFreeSpace() > 2000) { return new File(f, "log\\sumk"); } return new File("C:\\log\\sumk"); } return new File("/log/sumk"); } public static void appendLogObject(StringBuilder sb, LogObject log, String appId) throws IOException { UnsafeStringWriter stringWriter = new UnsafeStringWriter(sb); JsonWriter writer = new JsonWriter(stringWriter); writer.setSerializeNulls(false); writer.beginObject(); writer.name("name").value(log.loggerName); writer.name("date").value(log.logDate.to_yyyy_MM_dd_HH_mm_ss_SSS()); writer.name("userId").value(log.userId()); writer.name("traceId").value(log.traceId()); writer.name("spanId").value(log.spanId()); if (log.isTest()) { writer.name("test").value(1); } String body = log.body; writer.name("body").value(body); writer.name("threadName").value(log.threadName); writer.name("level").value(log.methodLevel.name()); writer.name("host").value(AppInfo.getLocalIp()); if (appId != null) { writer.name("appId").value(appId); } writer.name("pid").value(AppInfo.pid()); if (log.exception != null) { writer.name("exception").value(log.exception.getClass().getName()); StringBuilder sb2 = new StringBuilder(100); ExceptionUtil.printStackTrace(sb2, log.exception); writer.name("exceptiondetail").value(sb2.toString()); if (log.exception instanceof CodeException) { writer.name("exceptioncode").value(((CodeException) log.exception).getCode()); } } if (log.codeLine != null) { writer.name("className").value(log.codeLine.className); writer.name("methodName").value(log.codeLine.methodName); writer.name("lineNumber").value(log.codeLine.lineNumber); } Map attachs = log.attachments(); if (attachs != null) { for (Entry en : attachs.entrySet()) { writer.name("u_" + en.getKey()).value(en.getValue()); } } writer.endObject(); writer.flush(); writer.close(); } } ================================================ FILE: async-logger/src/main/java/org/yx/log/impl/UnionLogs.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log.impl; import java.util.Collections; import java.util.Objects; public final class UnionLogs { private static UnionLog unionLog = new DefaultUnionLog(); public static UnionLog getUnionLog() { return unionLog; } public static void setUnionLog(UnionLog unionLog) { UnionLogs.unionLog = Objects.requireNonNull(unionLog); } public static boolean start() { return unionLog.start(Collections.emptyMap()); } } ================================================ FILE: async-logger/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider ================================================ org.slf4j.v2.SumkServiceProvider ================================================ FILE: async-logger/src/test/java/org/test/ProxyLog.java ================================================ package org.test; import org.slf4j.spi.LocationAwareLogger; import org.yx.log.Log; public class ProxyLog { private static LocationAwareLogger log=(LocationAwareLogger)Log.get("proxy-log"); private static final String FQCN=ProxyLog.class.getName(); public static void log(String msg,Object... params) { log.log(null, FQCN, LocationAwareLogger.INFO_INT, msg, params, null); } public static void error(String msg,Throwable t) { log.log(null, FQCN, LocationAwareLogger.ERROR_INT, msg, null, t); } } ================================================ FILE: async-logger/src/test/java/org/test/Starter.java ================================================ package org.test; import org.junit.Test; import org.slf4j.spi.LocationAwareLogger; import org.yx.log.Log; import org.yx.util.SumkThreadPool; public class Starter { @Test public void test() { Log.get(this.getClass()).info("{} 只是个测试类",this.getClass().getSimpleName()); org.apache.log4j.Logger.getLogger(this.getClass()).info("这是log4j的测试类"); org.apache.logging.log4j.LogManager.getLogger(this.getClass()).warn("这是log4j 2.x的测试类"); org.apache.logging.log4j.LogManager.getLogger(this.getClass()).error("如果使用log4j 2,请引入log4j-to-slf4j"); ProxyLog.log("{}是{}的测试类","这个",LocationAwareLogger.class); ProxyLog.error("异常消息",new Exception("用于测试的异常")); Log.get(this.getClass()).atWarn().addArgument("李四").addKeyValue("key1", "marker1") .setMessage("name:{}").log(); SumkThreadPool.shutdown(); } } ================================================ FILE: async-logger/src/test/java/org/test/UnionDemo.java ================================================ package org.test; import java.util.List; import org.junit.Test; import org.yx.log.Log; import org.yx.log.impl.DefaultUnionLog; import org.yx.log.impl.UnionLogDao; import org.yx.log.impl.UnionLogObject; import org.yx.log.impl.UnionLogs; public class UnionDemo { @Test public void test() throws InterruptedException { DefaultUnionLog unionLog=(DefaultUnionLog) UnionLogs.getUnionLog(); unionLog.setDao(new UnionLogDao(){ @Override public void flush(boolean idle) { } @Override public void store(List logs) throws Exception { for(UnionLogObject log:logs){ System.out.println(log.getLog()); } } }); UnionLogs.start();//启动统一日志 // app.propertis中配置了unionTest写入到统一日志 Log.get("unionTest").info("abc"); Log.get("unionTest").info("222"); Thread.sleep(1000); } } ================================================ FILE: async-logger/src/test/resources/app.properties ================================================ sumk.ioc=org.yx #用,或;做分隔符 sumk.log.level=info,org.apache.zookeeper:warn sumk.log.console=1 #kv之间用=做分隔符,kv对之间用;做分隔符 s.log.day=path:D:\log\day-#.log; module:* #统一日志的配置 sumk.unionlog.module=unionTest ================================================ FILE: pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 pom com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots youtongluan 3205207767@qq.com https://www.oschina.net/p/sumk UTF-8 true 2.0.17 org.apache.commons commons-dbcp2 2.9.0 commons-logging commons-logging com.google.code.gson gson 2.10.1 mysql mysql-connector-java 8.0.33 org.ow2.asm asm 9.2 redis.clients jedis 3.7.0 org.slf4j slf4j-api ${slf4j-version} org.slf4j log4j-over-slf4j ${slf4j-version} org.slf4j jcl-over-slf4j ${slf4j-version} org.mybatis mybatis 3.5.0 org.apache.zookeeper zookeeper 3.9.3 io.netty netty-handler 4.1.107.Final com.101tec zkclient 0.11 org.eclipse.jetty jetty-servlet 9.4.51.v20230217 org.apache.httpcomponents httpasyncclient 4.0.2 org.apache.httpcomponents httpmime 4.2 junit junit 4.13.2 sumk-base sumk-framework sumk-db sumk-http sumk-rpc sumk-rpc-mina async-logger maven-compiler-plugin true 1.8 1.8 UTF-8 maven-resources-plugin copy-license process-sources copy-resources ${basedir}/target/classes/META-INF ${basedir} LICENSE maven-surefire-plugin true org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign false org.sonatype.central central-publishing-maven-plugin 0.8.0 true central ================================================ FILE: sql.dtd ================================================ ================================================ FILE: sumk-base/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: sumk-base/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-base com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots youtongluan 3205207767@qq.com https://www.oschina.net/p/sumk com.google.code.gson gson org.slf4j slf4j-api org.slf4j log4j-over-slf4j org.slf4j jcl-over-slf4j junit junit test ================================================ FILE: sumk-base/src/main/java/org/yx/annotation/doc/Comment.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.doc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 备注信息。目前可用于@Web、@Soa、@Table、@Column * */ @Target({ ElementType.METHOD, ElementType.TYPE, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Comment { String value(); } ================================================ FILE: sumk-base/src/main/java/org/yx/annotation/doc/NotNull.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.doc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 本注解是一种规范,约定该字段或者参数不为null。
* 比如某个参数不能为null,而内部的方法,可能为了性能考虑,并没有做非空判断 * */ @Target({ ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD, ElementType.LOCAL_VARIABLE }) @Retention(RetentionPolicy.SOURCE) @Documented public @interface NotNull { } ================================================ FILE: sumk-base/src/main/java/org/yx/base/Executable.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base; /** * Runnable的替代品 */ public interface Executable { void run() throws Throwable; } ================================================ FILE: sumk-base/src/main/java/org/yx/base/ItemJoiner.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base; public final class ItemJoiner { public static ItemJoiner create(CharSequence delimiter, CharSequence pre, CharSequence suf) { return new ItemJoiner(delimiter, pre, suf); } private StringBuilder sb = new StringBuilder(); private final CharSequence delimiter; private final CharSequence prefix; private final CharSequence suffix; private boolean hasDelimiter; public ItemJoiner(CharSequence delimiter, CharSequence pre, CharSequence suf) { this.delimiter = delimiter; this.prefix = pre; this.suffix = suf; } public ItemJoiner(CharSequence delimiter) { this(delimiter, null, null); } public ItemJoiner item() { if (sb.length() > 0) { sb.append(this.delimiter); hasDelimiter = true; } return this; } public ItemJoiner append(CharSequence v) { sb.append(v); return this; } public ItemJoiner append(char v) { sb.append(v); return this; } public ItemJoiner append(Object v) { sb.append(v); return this; } public ItemJoiner append(ItemJoiner item, CharSequence pre, CharSequence sub) { if (item == null) { return this; } if (item.hasDelimiter && pre != null) { sb.append(pre); } sb.append(item.toCharSequence()); if (item.hasDelimiter && sub != null) { sb.append(sub); } return this; } public CharSequence toCharSequence() { return this.toCharSequence(false); } public CharSequence toCharSequence(boolean forcePreAndSubFix) { if (sb == null || sb.length() == 0) { return null; } if (!forcePreAndSubFix && !hasDelimiter) { return sb; } int len = sb.length(); if (this.prefix != null) { len += this.prefix.length(); } if (this.suffix != null) { len += this.suffix.length(); } StringBuilder ret = new StringBuilder(len); if (this.prefix != null) { ret.append(this.prefix); } ret.append(sb); if (this.suffix != null) { ret.append(this.suffix); } return ret; } public boolean isEmpty() { return sb == null || sb.length() == 0; } @Override public String toString() { return String.valueOf(this.toCharSequence()); } public ItemJoiner appendNotEmptyItem(CharSequence item) { if (item == null || item.length() == 0) { return this; } this.item().append(item); return this; } public void append(long v) { this.append(String.valueOf(v)); } public void append(int v) { this.append(String.valueOf(v)); } public ItemJoiner copy() { return new ItemJoiner(delimiter, prefix, suffix); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/Lifecycle.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base; public interface Lifecycle { /** * 如果抛出异常,会导致启动失败 */ void start(); void stop(); } ================================================ FILE: sumk-base/src/main/java/org/yx/base/Ordered.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base; public interface Ordered extends Comparable { public static int DEFAULT_ORDER = 100; /** * 升序,值越大,优先级越低。一般不采用负数 * * @return 索引值 */ default int order() { return DEFAULT_ORDER; } @Override default int compareTo(Ordered o) { return Integer.compare(this.order(), o.order()); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/StartOnceLifecycle.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base; public abstract class StartOnceLifecycle implements Lifecycle { protected boolean started; @Override public final synchronized void start() { if (started) { return; } started = true; this.onStart(); } protected abstract void onStart(); @Override public void stop() { this.started = false; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/context/ActionContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.context; import java.util.Map; import org.yx.base.Executable; import org.yx.base.sumk.map.ListMap; import org.yx.exception.SumkException; import org.yx.log.Log; public final class ActionContext implements Cloneable { private LogContext logContext; /** * 用来做自增长的 */ private int spanSeed; public boolean isTest() { return logContext.test; } ActionContext(LogContext logContext) { this.logContext = logContext; } public String act() { return logContext.act; } public String traceId() { return logContext.traceId; } public String spanId() { return logContext.spanId; } private static final ThreadLocal holder = new ThreadLocal() { @Override protected ActionContext initialValue() { return new ActionContext(LogContext.EMPTY); } }; public static ActionContext newContext(String act, String traceId, boolean test) { return newContext(act, traceId, null, null, test, null); } public static ActionContext newContext(String act, String traceId, String spanId, String userId, boolean isTest, Map attachments) { LogContext logContext = LogContext.create(act, traceId, spanId, userId, isTest && AppContext.inst().isTest(), attachments); return newContext(logContext); } public static ActionContext newContext(LogContext logContext) { ActionContext c = new ActionContext(logContext); holder.set(c); return c; } public static Runnable wrap(Runnable r) { ActionContext ac = ActionContext.current(); return () -> { ActionContext.store(ac); try { r.run(); } finally { ActionContext.remove(); } }; } public static Runnable wrapExecutable(Executable r) { ActionContext ac = ActionContext.current(); return () -> { ActionContext.store(ac); try { r.run(); } catch (Throwable e) { Log.get("sumk.execute").error(r + "异步执行出错," + e.getLocalizedMessage(), e); } finally { ActionContext.remove(); } }; } public static void store(ActionContext c) { holder.set(c); } public static ActionContext current() { return holder.get(); } public static void remove() { holder.remove(); } public void setTraceIdIfAbsent(String traceId) { LogContext lc = this.logContext; if (lc.traceId != null) { return; } this.logContext = LogContext.create(lc.act, traceId, lc.spanId, lc.userId, lc.test, lc.attachments); } public String userId() { return logContext.userId; } public void userId(String userId) { LogContext lc = this.logContext; this.logContext = LogContext.create(lc.act, lc.traceId, lc.spanId, userId, lc.test, lc.attachments); } public Map attachmentView() { return logContext.attachments; } /** * 设置上下文的附加属性 * * @param key 序列化后的长度要在255以内 * @param value 序列化后的长度要在60K以内。如果value为null,就相当于remove */ public void setAttachment(String key, String value) { if (value == null) { if (this.logContext.attachments != null) { Map attachments = new ListMap<>(this.logContext.attachments); attachments.remove(key); this.logContext = LogContext.create(this.logContext, attachments); } return; } Map attachments = this.logContext.attachments; attachments = attachments == null ? new ListMap<>() : new ListMap<>(attachments, 1); attachments.put(key, value); this.logContext = LogContext.create(this.logContext, attachments); } public String getAttachment(String key) { Map attachments = this.logContext.attachments; if (attachments == null) { return null; } return attachments.get(key); } public String nextSpanId() { LogContext lc = this.logContext; if (lc.traceId == null) { return "1"; } int seed; synchronized (this) { seed = ++this.spanSeed; } String sp = lc.spanId; if (sp == null) { return String.valueOf(seed); } return new StringBuilder().append(lc.spanId).append('.').append(seed).toString(); } public LogContext logContext() { return this.logContext; } @Override public ActionContext clone() { try { return (ActionContext) super.clone(); } catch (CloneNotSupportedException e) { throw new SumkException(8234235, "clone not supported"); } } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/context/AppContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.context; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.yx.conf.AppInfo; /** * 这个只在系统启动的时候才能使用 */ public final class AppContext { private static AppContext inst = new AppContext(); private volatile boolean started; private volatile boolean destoryed; private boolean test; public static AppContext inst() { return inst; } private AppContext() { } private final ConcurrentMap map = new ConcurrentHashMap<>(); public void put(String key, Object obj) { map.put(key, obj); } public void put(Class clz, Object obj) { map.put(clz.getName(), obj); } public Object get(String key) { return map.get(key); } public Object get(Class clz) { return map.get(clz.getName()); } public T get(Class clz, T defaultObject) { String key = clz.getName(); Object old = map.get(key); return clz.isInstance(old) ? clz.cast(old) : defaultObject; } public static void clear() { inst = new AppContext(); } public static void startFailed() { if (AppInfo.getBoolean("sumk.exitIfStartFail", true)) { System.exit(1); } } public String getAppInfo(String key, String defaultV) { Object v = map.get(key); if (v instanceof String) { return (String) v; } return AppInfo.get(key, defaultV); } public boolean isStarted() { return started; } public boolean isDestoryed() { return destoryed; } public void resetStatus() { started = false; destoryed = false; } public void setStatusToStarted() { started = true; destoryed = false; } public void setStatusToDestoryed() { started = false; destoryed = true; } public boolean isTest() { return test; } public void setTest(boolean test) { this.test = test; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/context/LogContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.context; import java.util.Map; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public final class LogContext { static final LogContext EMPTY = new LogContext(null, null, null, null, false, null); public final String act; public final String traceId; public final String spanId; public final String userId; public final boolean test; final Map attachments; public static LogContext create(String act, String traceId, String spanId, String userId, boolean test, Map attachments) { if (attachments != null && attachments.size() > 0) { return new LogContext(act, traceId, spanId, userId, test, attachments); } if (act == null && traceId == null && spanId == null && userId == null && !test) { return EMPTY; } return new LogContext(act, traceId, spanId, userId, test, null); } public static LogContext create(LogContext lc, Map attachments) { return create(lc.act, lc.traceId, lc.spanId, lc.userId, lc.test, attachments); } private LogContext(String act, String traceId, String spanId, String userId, boolean test, Map attachments) { this.act = act; this.traceId = StringUtil.isEmpty(traceId) ? null : traceId; this.spanId = spanId; this.userId = userId; this.test = test; this.attachments = attachments == null ? null : CollectionUtil.unmodifyMap(attachments); } public static LogContext empty() { return EMPTY; } public Map unmodifiedAttachs() { return this.attachments; } public String getAct() { return act; } public String getTraceId() { return traceId; } public String getSpanId() { return spanId; } public String getUserId() { return userId; } public boolean isTest() { return test; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/DateAdapters.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import org.yx.util.SumkDate; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; public final class DateAdapters { public static void registerAll(GsonBuilder builder) { builder.registerTypeAdapter(LocalDateTime.class, localDateTimeAdapter); builder.registerTypeAdapter(LocalDate.class, localDateAdapter); builder.registerTypeAdapter(LocalTime.class, localTimeAdapter); builder.registerTypeAdapter(SumkDate.class, sumkDateAdapter); builder.registerTypeAdapter(java.sql.Date.class, sqlDateAdapter); } public static final TypeAdapter localDateTimeAdapter = new TypeAdapter() { @Override public void write(JsonWriter out, LocalDateTime value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(SumkDate.of(value).to_yyyy_MM_dd_HH_mm_ss_SSS()); } @Override public LocalDateTime read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String v = in.nextString(); return SumkDate.of(v).toLocalDateTime(); } }; public static final TypeAdapter localDateAdapter = new TypeAdapter() { @Override public void write(JsonWriter out, LocalDate value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(SumkDate.of(value, null).to_yyyy_MM_dd()); } @Override public LocalDate read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String v = in.nextString(); return SumkDate.of(v, SumkDate.yyyy_MM_dd).toLocalDate(); } }; public static final TypeAdapter localTimeAdapter = new TypeAdapter() { @Override public void write(JsonWriter out, LocalTime value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(SumkDate.of(null, value).to_HH_mm_ss_SSS()); } @Override public LocalTime read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String v = in.nextString(); return SumkDate.of(v, SumkDate.HH_mm_ss_SSS).toLocalTime(); } }; public static final TypeAdapter sumkDateAdapter = new TypeAdapter() { @Override public void write(JsonWriter out, SumkDate sd) throws IOException { if (sd == null) { out.nullValue(); return; } out.value(sd.to_yyyy_MM_dd_HH_mm_ss_SSS()); } @Override public SumkDate read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String v = in.nextString(); try { return SumkDate.of(v); } catch (Exception e) { String num = v; if (num.contains(".")) { num = num.substring(0, num.indexOf('.')); } return SumkDate.of(Long.parseLong(num)); } } }; public static final TypeAdapter sqlDateAdapter = new TypeAdapter() { @Override public void write(JsonWriter out, java.sql.Date d) throws IOException { if (d == null) { out.nullValue(); return; } out.value(SumkDate.of(d).to_yyyy_MM_dd()); } @Override public java.sql.Date read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String v = in.nextString(); return TimeUtil.toType(SumkDate.of(v, SumkDate.yyyy_MM_dd), java.sql.Date.class, true); } }; } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/DateFormater.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import org.yx.util.SumkDate; /** * yyyy-MM-dd */ public final class DateFormater implements SumkDateFormater { public static final DateFormater inst = new DateFormater(); @Override public int order() { return 50; } @Override public SumkDate parse(String text) { int firstIndex = text.length() - 6; int year = Integer.parseInt(text.substring(0, firstIndex)); int month = Integer.parseInt(text.substring(firstIndex + 1, firstIndex + 3)); int day = Integer.parseInt(text.substring(firstIndex + 4)); return SumkDate.of(year, month, day, 0, 0, 0, 0); } @Override public boolean accept(String format) { return format.length() == 10 && format.regionMatches(0, "yyyy", 0, 4) && format.regionMatches(5, "MM", 0, 2) && format.endsWith("dd"); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/DateTimeFormater.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import org.yx.util.SumkDate; /** * yyyy-MM-dd HH:mm:ss */ public final class DateTimeFormater implements SumkDateFormater { public static final DateTimeFormater inst = new DateTimeFormater(); private DateTimeFormater() { } @Override public int order() { return 20; } @Override public SumkDate parse(String text) { int firstIndex = text.length() - 15; int year = Integer.parseInt(text.substring(0, firstIndex)); int month = Integer.parseInt(text.substring(firstIndex + 1, firstIndex + 3)); int day = Integer.parseInt(text.substring(firstIndex + 4, firstIndex + 6)); int hour = Integer.parseInt(text.substring(firstIndex + 7, firstIndex + 9)); int minute = Integer.parseInt(text.substring(firstIndex + 10, firstIndex + 12)); int second = Integer.parseInt(text.substring(firstIndex + 13)); return SumkDate.of(year, month, day, hour, minute, second, 0); } @Override public boolean accept(String format) { return format.length() == 19 && format.regionMatches(0, "yyyy", 0, 4) && format.regionMatches(5, "MM", 0, 2) && format.regionMatches(8, "dd", 0, 2) && format.regionMatches(11, "HH", 0, 2) && format.regionMatches(14, "mm", 0, 2) && format.endsWith("ss"); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/DateTimeTypeAdapter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import java.io.IOException; import java.util.Date; import org.yx.util.StringUtil; import org.yx.util.SumkDate; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; public final class DateTimeTypeAdapter extends TypeAdapter { private String dateFormat = SumkDate.yyyy_MM_dd_HH_mm_ss_SSS; public void setDateFormat(String format) { if (StringUtil.isEmpty(format)) { this.dateFormat = null; } this.dateFormat = format; } @Override public Date read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } return deserializeToDate(in.nextString()); } private Date deserializeToDate(final String text) throws IOException { try { return SumkDate.of(text).toDate(); } catch (Exception e) { } String num = text; if (num.contains(".")) { num = num.substring(0, num.indexOf('.')); } return new Date(Long.parseLong(num)); } @Override public void write(JsonWriter out, Date value) throws IOException { if (value == null) { out.nullValue(); return; } if (this.dateFormat == null) { out.value(value.getTime()); return; } out.value(SumkDate.format(value, this.dateFormat)); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/FullDateTimeFormater.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import org.yx.util.StringUtil; import org.yx.util.SumkDate; public final class FullDateTimeFormater implements SumkDateFormater { public static final FullDateTimeFormater inst = new FullDateTimeFormater(); private FullDateTimeFormater() { } @Override public int order() { return 0; } @Override public SumkDate parse(String text) { int dotIndex = text.length() - 1; for (; dotIndex > 15; dotIndex--) { char c = text.charAt(dotIndex); if (!StringUtil.isNumber(c)) { break; } } String textMil = text.substring(dotIndex + 1); int mils = Integer.parseInt(textMil); if (textMil.length() == 1) { mils *= 100; } else if (textMil.length() == 2) { mils *= 10; } int second = Integer.parseInt(text.substring(dotIndex - 2, dotIndex)); int minute = Integer.parseInt(text.substring(dotIndex - 5, dotIndex - 3)); int hour = Integer.parseInt(text.substring(dotIndex - 8, dotIndex - 6)); int day = Integer.parseInt(text.substring(dotIndex - 11, dotIndex - 9)); int month = Integer.parseInt(text.substring(dotIndex - 14, dotIndex - 12)); int year = Integer.parseInt(text.substring(0, dotIndex - 15)); return SumkDate.of(year, month, day, hour, minute, second, mils); } @Override public boolean accept(String format) { return format.length() == 23 && format.regionMatches(0, "yyyy", 0, 4) && format.regionMatches(5, "MM", 0, 2) && format.regionMatches(8, "dd", 0, 2) && format.regionMatches(11, "HH", 0, 2) && format.regionMatches(14, "mm", 0, 2) && format.regionMatches(17, "ss", 0, 2) && format.endsWith("SSS"); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/SumkDateFormater.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import org.yx.base.Ordered; import org.yx.util.SumkDate; public interface SumkDateFormater extends Ordered { SumkDate parse(String text); boolean accept(String format); } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/SumkDateQuery.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; import java.time.temporal.TemporalQuery; import org.yx.util.SumkDate; public class SumkDateQuery implements TemporalQuery { public static final SumkDateQuery inst = new SumkDateQuery(); @Override public SumkDate queryFrom(TemporalAccessor temporal) { if (temporal instanceof LocalDateTime) { return SumkDate.of((LocalDateTime) temporal); } else if (temporal instanceof ZonedDateTime) { LocalDateTime dt = ((ZonedDateTime) temporal).toLocalDateTime(); return SumkDate.of(dt); } else if (temporal instanceof OffsetDateTime) { LocalDateTime dt = ((OffsetDateTime) temporal).toLocalDateTime(); return SumkDate.of(dt); } LocalDate date = temporal.query(TemporalQueries.localDate()); LocalTime time = temporal.query(TemporalQueries.localTime()); if (date == null && time == null) { return null; } return SumkDate.of(date, time); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/date/TimeUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.date; import java.sql.Time; import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.util.Date; import org.yx.exception.SumkException; import org.yx.util.SumkDate; public class TimeUtil { public static final int DATETIME_CONVERT = 912753916; public static boolean isGenericDate(Class type) { return type == Date.class || type == java.sql.Date.class || type == Time.class || type == Timestamp.class || type == LocalDate.class || type == LocalTime.class || type == LocalDateTime.class || type == SumkDate.class; } @SuppressWarnings("unchecked") public static T toType(Object v, Class type, boolean failIfNotSupport) { if (v.getClass() == type) { return (T) v; } if (type == SumkDate.class) { return toSumkDate(v, failIfNotSupport); } if (v instanceof Date) { return toType((Date) v, type, failIfNotSupport); } Class sourceClz = v.getClass(); if (LocalDateTime.class == sourceClz) { return toType((LocalDateTime) v, type, failIfNotSupport); } if (LocalDate.class == sourceClz) { return toType((LocalDate) v, type, failIfNotSupport); } if (LocalTime.class == sourceClz) { return toType((LocalTime) v, type, failIfNotSupport); } if (SumkDate.class == sourceClz) { return toType((SumkDate) v, type, failIfNotSupport); } if (failIfNotSupport || !isGenericDate(type)) { throw new SumkException(1234345, type.getClass().getName() + " cannot convert to " + type.getName()); } return (T) v; } @SuppressWarnings("unchecked") private static T toSumkDate(Object v, boolean failIfNotSupport) { if (v instanceof Date) { return (T) SumkDate.of((Date) v); } if (v instanceof LocalDateTime) { return (T) SumkDate.of((LocalDateTime) v); } if (v instanceof LocalDate) { return (T) SumkDate.of((LocalDate) v, null); } if (v instanceof LocalTime) { return (T) SumkDate.of(null, (LocalTime) v); } if (failIfNotSupport) { throw new SumkException(63414353, v.getClass().getName() + "is not supported Date type"); } return (T) v; } @SuppressWarnings({ "unchecked", "deprecation" }) private static T toType(SumkDate v, Class type, boolean failIfNotSupport) { if (Date.class == type) { return (T) v.toDate(); } if (java.sql.Date.class == type) { return (T) java.sql.Date.valueOf(v.toLocalDate()); } if (Timestamp.class == type) { return (T) v.toTimestamp(); } if (Time.class == type) { return (T) new Time(v.getHour(), v.getMinute(), v.getSecond()); } if (LocalDate.class == type) { return (T) v.toLocalDate(); } if (LocalDateTime.class == type) { return (T) v.toLocalDateTime(); } if (LocalTime.class == type) { return (T) v.toLocalTime(); } if (failIfNotSupport || !isGenericDate(type)) { throw new SumkException(63414353, type.getClass().getName() + "is not supported Date type"); } return (T) v; } @SuppressWarnings({ "unchecked", "deprecation" }) private static T toType(Date v, Class type, boolean failIfNotSupport) { long time = v.getTime(); if (Date.class == type) { return (T) new Date(time); } if (java.sql.Date.class == type) { if (Time.class == v.getClass()) { throw new SumkException(DATETIME_CONVERT, "Time cannot convert to java.sql.Date"); } return (T) new java.sql.Date(v.getYear(), v.getMonth(), v.getDate()); } if (Timestamp.class == type) { return (T) new Timestamp(time); } if (Time.class == type) { if (java.sql.Date.class == v.getClass()) { throw new SumkException(DATETIME_CONVERT, "java.sql.Date cannot convert to Time"); } return (T) new Time(v.getHours(), v.getMinutes(), v.getSeconds()); } SumkDate sumk = SumkDate.of(v); if (LocalDate.class == type) { if (Time.class == v.getClass()) { throw new SumkException(DATETIME_CONVERT, "Time cannot convert to LocalDate"); } return (T) sumk.toLocalDate(); } if (LocalDateTime.class == type) { return (T) sumk.toLocalDateTime(); } if (LocalTime.class == type) { if (java.sql.Date.class == v.getClass()) { throw new SumkException(DATETIME_CONVERT, "java.sql.Date cannot convert to LocalTime"); } return (T) sumk.toLocalTime(); } if (failIfNotSupport || !isGenericDate(type)) { throw new SumkException(63414353, type.getClass().getName() + "is not supported Date type"); } return (T) v; } @SuppressWarnings("unchecked") private static T toType(LocalDateTime v, Class type, boolean failIfNotSupport) { if (Date.class == type) { return (T) Date.from(v.atZone(ZoneId.systemDefault()).toInstant()); } if (java.sql.Date.class == type) { return (T) java.sql.Date.valueOf(v.toLocalDate()); } if (Timestamp.class == type) { return (T) Timestamp.valueOf(v); } if (Time.class == type) { return (T) Time.valueOf(v.toLocalTime()); } if (LocalDate.class == type) { return (T) v.toLocalDate(); } if (LocalTime.class == type) { return (T) v.toLocalTime(); } if (failIfNotSupport || !isGenericDate(type)) { throw new SumkException(DATETIME_CONVERT, type.getClass().getName() + "is not a supported Date type"); } return (T) v; } @SuppressWarnings({ "unchecked", "deprecation" }) private static T toType(LocalDate v, Class type, boolean failIfNotSupport) { if (Date.class == type) { return (T) new Date(v.getYear() - 1900, v.getMonthValue() - 1, v.getDayOfMonth()); } if (java.sql.Date.class == type) { return (T) java.sql.Date.valueOf(v); } if (Timestamp.class == type) { LocalDateTime dt = LocalDateTime.of(v, LocalTime.of(0, 0)); return (T) Timestamp.valueOf(dt); } if (Time.class == type) { throw new SumkException(DATETIME_CONVERT, "LocalDate cannot convert to Time"); } if (LocalDateTime.class == type) { return (T) LocalDateTime.of(v, LocalTime.of(0, 0)); } if (LocalTime.class == type) { throw new SumkException(DATETIME_CONVERT, "LocalDate cannot convert to LocalTime"); } if (failIfNotSupport || !isGenericDate(type)) { throw new SumkException(DATETIME_CONVERT, type.getClass().getName() + "is not a supported Date type"); } return (T) v; } @SuppressWarnings("unchecked") private static T toType(LocalTime v, Class type, boolean failIfNotSupport) { if (Date.class == type) { return (T) new Date(Time.valueOf(v).getTime()); } if (java.sql.Date.class == type) { throw new SumkException(DATETIME_CONVERT, "LocalTime cannot convert to java.sql.Date"); } if (Timestamp.class == type) { return (T) new Timestamp(Time.valueOf(v).getTime()); } if (Time.class == type) { return (T) Time.valueOf(v); } if (LocalDateTime.class == type) { return (T) LocalDateTime.of(LocalDate.ofEpochDay(0), v); } if (LocalDate.class == type) { throw new SumkException(DATETIME_CONVERT, "LocalTime cannot convert to LocalDate"); } if (failIfNotSupport || !isGenericDate(type)) { throw new SumkException(DATETIME_CONVERT, type.getClass().getName() + "is not a supported Date type"); } return (T) v; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/matcher/BooleanMatcher.java ================================================ package org.yx.base.matcher; import java.util.function.Predicate; public enum BooleanMatcher implements Predicate { FALSE(false), TRUE(true); private final boolean matched; private BooleanMatcher(boolean matched) { this.matched = matched; } @Override public boolean test(String text) { return matched; } @Override public Predicate negate() { return this.matched ? FALSE : TRUE; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/matcher/InOutMatcher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.matcher; import java.util.Objects; import java.util.function.Predicate; public class InOutMatcher implements Predicate { private final Predicate include; private final Predicate exclude; InOutMatcher(Predicate include, Predicate exclude) { this.include = Objects.requireNonNull(include); this.exclude = Objects.requireNonNull(exclude); } @Override public boolean test(String t) { if (exclude.test(t)) { return false; } return include.test(t); } @Override public String toString() { return "[include: " + include + ", exclude: " + exclude + "]"; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/matcher/Matchers.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.matcher; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.Predicate; import org.yx.base.matcher.WildcardMatcher.HeadAndTail; import org.yx.log.RawLog; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class Matchers { /** * 表达式的分隔符 */ public static final String SPLIT = ","; public static final String WILDCARD = "*"; public static Predicate createWildcardMatcher(String patterns) { return createWildcardMatcher(patterns, 1); } public static Predicate createWildcardMatcher(String patterns, int minPatternLength) { if (patterns == null || patterns.isEmpty()) { return BooleanMatcher.FALSE; } String[] array = StringUtil.toLatin(patterns).split(SPLIT); return createWildcardMatcher(CollectionUtil.unmodifyList(array), minPatternLength); } /** * 创建表达式。这里的表达式不做全角半角处理,也不对逗号做分割,但允许包含null * * @param patterns 表达式字符串列表,支持为null。这里的每一个都是最小的表达式,不支持内部再含有逗号 * @param minPatternLength 最小长度,会忽略pattern长度小于这个最小长度的 * @return 表达式判断器 */ public static Predicate createWildcardMatcher(Collection patterns, int minPatternLength) { if (patterns == null || patterns.isEmpty()) { return BooleanMatcher.FALSE; } Set exact = new HashSet<>(); Set matchStart = new HashSet<>(); Set matchEnd = new HashSet<>(); Set matchContain = new HashSet<>(); Set headAndTailMatch = new HashSet<>(); final String doubleWild = WILDCARD + WILDCARD; for (String s : patterns) { if (s == null || (s = s.trim()).isEmpty()) { continue; } if (s.length() < minPatternLength) { RawLog.warn("sumk.conf", "[" + s + "]的长度太短,被忽略.最小长度为:" + minPatternLength); continue; } if (WILDCARD.equals(s) || doubleWild.equals(s)) { return BooleanMatcher.TRUE; } int wildCount = s.length() - s.replace(WILDCARD, "").length(); if (wildCount > 2) { RawLog.warn("sumk.conf", "[" + s + "]的*出现次数超过2次,将被忽略"); } if (wildCount == 0) { exact.add(s); continue; } int firstIndex = s.indexOf(WILDCARD); if (wildCount == 1) { if (firstIndex == 0) { matchEnd.add(s.substring(1)); continue; } if (firstIndex == s.length() - 1) { matchStart.add(s.substring(0, firstIndex)); continue; } HeadAndTail h = new HeadAndTail(s.substring(0, firstIndex), s.substring(firstIndex + 1)); headAndTailMatch.add(h); } if (wildCount == 2) { if (firstIndex == 0 && s.endsWith(WILDCARD)) { matchContain.add(s.substring(1, s.length() - 1)); continue; } RawLog.warn("sumk.conf", "[" + s + "]的*不出现了2次,但不在头尾,将被忽略"); continue; } } if (exact.isEmpty() && matchStart.isEmpty() && matchEnd.isEmpty() && matchContain.isEmpty() && headAndTailMatch.isEmpty()) { return BooleanMatcher.FALSE; } return new WildcardMatcher(exact, matchStart, matchEnd, matchContain, headAndTailMatch); } /** * 创建InOutMatcher、BooleanMatcher或其它Matcher * * @param include 可以匹配的Predicate,不能为null * @param exclude 被排除的Predicate,不能为null * @return 返回值有可能是InOutMatcher类型,也有可能是BooleanMatcher */ public static Predicate includeAndExclude(Predicate include, Predicate exclude) { if (exclude == BooleanMatcher.FALSE) { return include; } if (include == BooleanMatcher.FALSE || exclude == BooleanMatcher.TRUE) { return BooleanMatcher.FALSE; } return new InOutMatcher(include, exclude); } public static Predicate includeAndExclude(String include, String exclude) { return includeAndExclude(createWildcardMatcher(include), createWildcardMatcher(exclude)); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/matcher/WildcardMatcher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.matcher; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import org.yx.base.ItemJoiner; import org.yx.util.CollectionUtil; /** * 或类型的匹配,只要能匹配中一个条件,就认为匹配成功 * */ public class WildcardMatcher implements Predicate { private final Set exacts; private final String[] matchStarts; private final String[] matchEnds; private final String[] contains; private final HeadAndTail[] headTails; WildcardMatcher(Set exacts, Set matchStart, Set matchEnd, Set matchContain, Set headAndTailMatch) { this.exacts = CollectionUtil.isEmpty(exacts) ? null : new HashSet<>(exacts); this.matchStarts = toArray(matchStart); this.matchEnds = toArray(matchEnd); this.contains = toArray(matchContain); this.headTails = CollectionUtil.isEmpty(headAndTailMatch) ? null : headAndTailMatch.toArray(new HeadAndTail[headAndTailMatch.size()]); } private String[] toArray(Collection src) { if (CollectionUtil.isEmpty(src)) { return null; } return src.toArray(new String[src.size()]); } @Override public boolean test(String text) { if (this.exacts != null && this.exacts.contains(text)) { return true; } if (this.matchStarts != null) { for (String start : this.matchStarts) { if (text.startsWith(start)) { return true; } } } if (this.matchEnds != null) { for (String end : this.matchEnds) { if (text.endsWith(end)) { return true; } } } if (this.contains != null) { for (String c : this.contains) { if (text.contains(c)) { return true; } } } if (this.headTails != null) { for (HeadAndTail ht : this.headTails) { if (ht.test(text)) { return true; } } } return false; } public Collection exacts() { return exacts == null ? null : Collections.unmodifiableSet(exacts); } public List matchStarts() { return CollectionUtil.unmodifyList(matchStarts); } public List matchEnds() { return CollectionUtil.unmodifyList(matchEnds); } public List contains() { return CollectionUtil.unmodifyList(contains); } @Override public String toString() { ItemJoiner join = new ItemJoiner(","); if (exacts != null) { join.item().append("exacts:").append(exacts); } if (matchStarts != null) { join.item().append("matchStarts:").append(Arrays.toString(matchStarts)); } if (matchEnds != null) { join.item().append("matchEnds:").append(Arrays.toString(matchEnds)); } if (contains != null) { join.item().append("contains:").append(Arrays.toString(contains)); } if (headTails != null) { join.item().append("headTails:").append(Arrays.toString(headTails)); } return join.toString(); } public static final class HeadAndTail implements Predicate { final String head; final String tail; public HeadAndTail(String head, String tail) { this.head = Objects.requireNonNull(head); this.tail = Objects.requireNonNull(tail); } @Override public boolean test(String text) { if (text.length() < head.length() + tail.length()) { return false; } return text.startsWith(head) && text.endsWith(tail); } @Override public String toString() { return "HeadAndTail [head=" + head + ", tail=" + tail + "]"; } } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/scaner/ClassScaner.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.scaner; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.function.Function; import org.yx.util.Loader; public final class ClassScaner { private static Function, Collection> scaner = new FileNameScaner(".class"); public static Function, Collection> getScaner() { return scaner; } public static void setScaner(Function, Collection> scaner) { ClassScaner.scaner = Objects.requireNonNull(scaner); } public static Set> subClassesInSameOrSubPackage(Class baseClz) throws ClassNotFoundException { return list(baseClz.getPackage().getName(), baseClz); } @SuppressWarnings("unchecked") public static Set> list(String packageName, Class baseClz) throws ClassNotFoundException { Collection clzNames = listClasses(Collections.singletonList(packageName)); if (clzNames == null || clzNames.isEmpty()) { return Collections.emptySet(); } Set names = new HashSet<>(clzNames); Set> set = new HashSet<>(); for (String className : names) { Class clz = Loader.loadClass(className); if (baseClz.isAssignableFrom(clz) && (!clz.isInterface()) && ((clz.getModifiers() & Modifier.ABSTRACT) == 0)) { set.add((Class) clz); } } return set; } public static Collection listClasses(Collection packageNames) { return scaner.apply(packageNames); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/scaner/FileNameScaner.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.scaner; import java.io.File; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.slf4j.Logger; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.Loader; import org.yx.util.StringUtil; public final class FileNameScaner implements Function, Collection> { private final String subfix; public FileNameScaner(String subfix) { this.subfix = Objects.requireNonNull(subfix); } @Override public Collection apply(Collection packageNames) { Logger log = Logs.system(); Set classNameSet = new HashSet<>(240); if (packageNames == null || packageNames.isEmpty()) { return classNameSet; } String packagePath; File file; URL url; Enumeration eUrl; for (String packageName : packageNames) { packagePath = StringUtil.toLatin(packageName).trim().replace('.', '/'); if (packagePath.isEmpty()) { continue; } if (!packagePath.endsWith("/")) { packagePath += "/"; } try { eUrl = Loader.getResources(packagePath); while (eUrl.hasMoreElements()) { url = eUrl.nextElement(); log.debug("find {}", url.getFile()); if (JarFileUtil.URL_PROTOCOL_JAR.equals(url.getProtocol()) || JarFileUtil.URL_PROTOCOL_ZIP.equals(url.getProtocol())) { this.findClassInJar(classNameSet, url, packagePath); continue; } file = new File(url.toURI()); if (!file.exists()) { throw new SumkException(9723423, file.getAbsolutePath() + " is not a file"); } this.parseFile(classNameSet, file, packagePath); } } catch (Exception ex) { log.error("parse " + packageName + "failed", ex); throw new SumkException(23423, ex.getMessage(), ex); } } return classNameSet; } private void parseFile(Collection classNameCol, final File root, final String packagePath) { File[] subFiles = root.listFiles(); if (subFiles == null || subFiles.length == 0) { return; } for (File file : subFiles) { if (file.isDirectory()) { this.parseFile(classNameCol, file, packagePath); } else if (file.getName().endsWith(subfix)) { String absolutePath = file.getAbsolutePath().replace('\\', '/'); int index = absolutePath.indexOf('/' + packagePath) + 1; if (index < 1) { Logs.system().error(absolutePath + " donot contain " + packagePath); continue; } addClz(classNameCol, absolutePath.substring(index)); } } } private void addClz(Collection classNameList, String classPath) { Logger log = Logs.system(); if (!classPath.endsWith(subfix) || classPath.contains("$")) { if (log.isTraceEnabled()) { log.trace("文件{}不满足条件", classPath); } return; } String className = classPath.substring(0, classPath.length() - this.subfix.length()).replace('/', '.'); if (!classNameList.contains(className)) { classNameList.add(className); } } private void findClassInJar(Collection classNameList, URL url, String packagePath) throws IOException { Logger log = Logs.system(); JarFile jarFile = null; try { URLConnection conn = url.openConnection(); if (!JarURLConnection.class.isInstance(conn)) { log.error("the connection of {} is {}", url.getPath(), conn.getClass().getName()); throw new SumkException(25345643, conn.getClass().getName() + " is not JarURLConnection"); } jarFile = ((JarURLConnection) conn).getJarFile(); Enumeration jarEntryEnum = jarFile.entries(); while (jarEntryEnum.hasMoreElements()) { String entityName = jarEntryEnum.nextElement().getName(); if (entityName.startsWith("/")) { entityName = entityName.substring(1); } if (entityName.startsWith(packagePath)) { addClz(classNameList, entityName); } else { if (log.isTraceEnabled()) { log.trace("{}不满足条件", entityName); } } } } finally { if (jarFile != null) { jarFile.close(); } } } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/scaner/JarFileUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.scaner; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.yx.util.IOUtil; public class JarFileUtil { public static final String URL_PROTOCOL_JAR = "jar"; public static final String URL_PROTOCOL_ZIP = "zip"; public static final String JAR_URL_SEPARATOR = "!/"; public static boolean isJarURL(URL url) { String protocol = url.getProtocol(); return URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol); } public static Map exactResourcesInJar(URL url, Predicate tester) throws IOException { Map map = new HashMap<>(); JarFile jarFile = null; try { String prefix = ""; String urlPath = url.getPath(); int separatorIndex = urlPath.lastIndexOf(JAR_URL_SEPARATOR); if (separatorIndex != -1 && separatorIndex + JAR_URL_SEPARATOR.length() < urlPath.length()) { prefix = urlPath.substring(separatorIndex + JAR_URL_SEPARATOR.length()); if (!prefix.endsWith("/")) { prefix += "/"; } } jarFile = ((JarURLConnection) url.openConnection()).getJarFile(); Enumeration jarEntryEnum = jarFile.entries(); while (jarEntryEnum.hasMoreElements()) { JarEntry entry = jarEntryEnum.nextElement(); String entityName = entry.getName(); if (entry.isDirectory() || !entityName.startsWith(prefix)) { continue; } if (tester != null && !tester.test(entityName)) { continue; } byte[] bs = IOUtil.readAllBytes(jarFile.getInputStream(entry), true); map.put(entityName, bs); } } finally { if (jarFile != null) { jarFile.close(); } } return map; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/sumk/UnmodifiableArrayList.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.sumk; import java.io.Serializable; import java.util.AbstractList; import java.util.Collection; import java.util.Comparator; import java.util.Objects; import java.util.RandomAccess; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; public final class UnmodifiableArrayList extends AbstractList implements RandomAccess, Serializable { private static final long serialVersionUID = 1L; private final Object[] elements; public UnmodifiableArrayList(E[] array) { elements = Objects.requireNonNull(array); } public UnmodifiableArrayList(Collection col) { this.elements = col.toArray(); } @Override public int size() { return elements.length; } @Override public Object[] toArray() { return elements.clone(); } @Override public T[] toArray(T[] a) { System.arraycopy(this.elements, 0, a, 0, size()); return a; } @SuppressWarnings("unchecked") @Override public E get(int index) { return (E) elements[index]; } @Override public int indexOf(Object o) { Object[] a = this.elements; if (o == null) { for (int i = 0; i < a.length; i++) if (a[i] == null) return i; } else { for (int i = 0; i < a.length; i++) if (o.equals(a[i])) return i; } return -1; } @Override public boolean contains(Object o) { return indexOf(o) != -1; } @Override public Spliterator spliterator() { return Spliterators.spliterator(elements, Spliterator.ORDERED); } @SuppressWarnings("unchecked") @Override public void forEach(Consumer action) { Objects.requireNonNull(action); for (Object e : elements) { action.accept((E) e); } } @Override public void sort(Comparator c) { throw new UnsupportedOperationException(); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/sumk/UnsafeByteArrayOutputStream.java ================================================ package org.yx.base.sumk; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Arrays; public class UnsafeByteArrayOutputStream extends OutputStream { private byte[] buf; private int count; public UnsafeByteArrayOutputStream(int size) { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); } buf = new byte[size]; } private void ensureCapacity(int minCapacity) { if (minCapacity - buf.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { int oldCapacity = buf.length; int newCapacity = oldCapacity << 1; if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); buf = Arrays.copyOf(buf, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } public void write(int b) { ensureCapacity(count + 1); buf[count] = (byte) b; count += 1; } public void write(byte b[], int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) - b.length > 0)) { throw new IndexOutOfBoundsException(); } ensureCapacity(count + len); System.arraycopy(b, off, buf, count, len); count += len; } public void writeTo(OutputStream out) throws IOException { out.write(buf, 0, count); } public void reset() { count = 0; } public byte[] toByteArray() { return Arrays.copyOf(buf, count); } public int size() { return count; } public String toString() { return new String(buf, 0, count); } public String toString(String charsetName) throws UnsupportedEncodingException { return new String(buf, 0, count, charsetName); } public byte[] extractHttpBodyData() { int dataLength = this.count; if (dataLength == 0) { return new byte[0]; } byte[] bs = this.buf; if (dataLength > 4 && bs[0] == 100 && bs[1] == 97 && bs[2] == 116 && bs[3] == 97 && bs[4] == 61) { byte[] temp = new byte[dataLength - 5]; System.arraycopy(bs, 5, temp, 0, temp.length); return temp; } return dataLength == bs.length ? bs : Arrays.copyOf(bs, dataLength); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/sumk/UnsafeStringWriter.java ================================================ package org.yx.base.sumk; import java.io.IOException; import java.io.Writer; public class UnsafeStringWriter extends Writer { private final StringBuilder buf; public UnsafeStringWriter() { this(32); } public UnsafeStringWriter(int initialSize) { if (initialSize < 0) { throw new IllegalArgumentException("Negative buffer size"); } buf = new StringBuilder(initialSize); lock = buf; } public UnsafeStringWriter(StringBuilder sb) { this.buf = sb; this.lock = buf; } public void write(int c) { buf.append((char) c); } public void write(char cbuf[], int off, int len) { if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } buf.append(cbuf, off, len); } public void write(String str) { buf.append(str); } public void write(String str, int off, int len) { buf.append(str.substring(off, off + len)); } public UnsafeStringWriter append(CharSequence csq) { if (csq == null) { write("null"); } else { buf.append(csq); } return this; } public UnsafeStringWriter append(CharSequence csq, int start, int end) { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } public UnsafeStringWriter append(char c) { write(c); return this; } public String toString() { return buf.toString(); } public StringBuilder getBuffer() { return buf; } public void flush() { } /** * 空方法,close后不影响使用 */ public void close() throws IOException { } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/sumk/map/ListEntrySet.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.sumk.map; import java.util.AbstractSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Objects; public class ListEntrySet extends AbstractSet> { protected final List> list; public ListEntrySet(List> list) { this.list = Objects.requireNonNull(list); } @Override public boolean add(Entry e) { Entry old = this.getByKey(e.getKey()); if (old != null) { old.setValue(e.getValue()); return true; } return list.add(e); } @Override public int size() { return list.size(); } public Entry getByKey(K key) { int index = this.indexKey(key); if (index < 0) { return null; } return list.get(index); } public int indexKey(K key) { final int size = list.size(); for (int i = 0; i < size; i++) { Entry en = list.get(i); if (Objects.equals(en.getKey(), key)) { return i; } } return -1; } @Override public Iterator> iterator() { return list.iterator(); } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/sumk/map/ListMap.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.sumk.map; import java.io.Serializable; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * 支持key、value为null。 适用于很少调用get、remove甚至put的场景。或者map很小。 * 它的优势是占用内存小,并且iterator的性能比HashMap还好 */ public class ListMap extends AbstractMap implements Serializable { private static final long serialVersionUID = 1L; private final ListEntrySet data; protected ListMap(ListEntrySet data) { this.data = Objects.requireNonNull(data); } public ListMap() { this.data = new ListEntrySet(new ArrayList<>()); } public ListMap(int initialCapacity) { this.data = new ListEntrySet(new ArrayList<>(initialCapacity)); } public ListMap(Map map) { this(map, 0); } public ListMap(Map map, int estimateGrowSize) { List> list = new ArrayList<>(map.size() + estimateGrowSize); for (Entry en : map.entrySet()) { list.add(new AbstractMap.SimpleEntry<>(en)); } this.data = new ListEntrySet(list); } @Override public Set> entrySet() { return data; } @Override public V put(K key, V value) { Entry old = data.getByKey(key); V v = old != null ? old.getValue() : null; data.add(new AbstractMap.SimpleEntry<>(key, value)); return v; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/sumk/map/UnmodifiableListMap.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.sumk.map; import java.io.Serializable; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.yx.util.CollectionUtil; /** * 支持key、value为null */ public class UnmodifiableListMap extends AbstractMap implements Serializable { private static final long serialVersionUID = 1L; private final ListEntrySet data; public UnmodifiableListMap(Map map) { List> list = new ArrayList<>(map.size()); for (Entry en : map.entrySet()) { list.add(new AbstractMap.SimpleImmutableEntry<>(en)); } this.data = new ListEntrySet<>(CollectionUtil.unmodifyList(list)); } @Override public Set> entrySet() { return data; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/thread/PriorityRunnable.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.thread; import java.util.Objects; import org.yx.log.Log; public class PriorityRunnable implements Runnable { private int priority; private final Runnable target; private final int threshold; public PriorityRunnable(Runnable target, int priority, int threshold) { this.target = Objects.requireNonNull(target, "target is null"); this.priority = priority; this.threshold = threshold; } @Override public void run() { if (this.priority < threshold) { String msg = new StringBuilder().append("Task ").append(toString()).append(" discarded, because of ") .append(priority).append(" lower than ").append(threshold).toString(); Log.get("sumk.thread").warn(msg); return; } target.run(); } int priority() { return priority; } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/thread/SumkExecutorService.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.thread; import java.util.concurrent.ExecutorService; public interface SumkExecutorService extends ExecutorService, ThresholdExecutor { int getCorePoolSize(); void setCorePoolSize(int size); int getMaximumPoolSize(); void setMaximumPoolSize(int size); boolean allowsCoreThreadTimeOut(); void allowCoreThreadTimeOut(boolean allowCoreTimeout); int getPoolSize(); int getQueued(); } ================================================ FILE: sumk-base/src/main/java/org/yx/base/thread/ThreadPools.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.thread; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.yx.conf.AppInfo; import org.yx.log.Log; import org.yx.util.SumkThreadPool; public class ThreadPools { public static final SumkExecutorService DEFAULT_EXECUTOR = ThreadPools.create("sumk", 50, 500, Integer.getInteger("sumk.thread.pool.keepAliveTime", 30000)); public static SumkExecutorService create(String name, int core, int max, int aliveTime) { AbortPolicyQueue abortPolicyQueue = new AbortPolicyQueue(AppInfo.getInt("sumk.threadpool.maxqueue", 100000), core); ThresholdThreadPool pool = new ThresholdThreadPool(core, max, aliveTime, TimeUnit.MILLISECONDS, abortPolicyQueue, SumkThreadPool.createThreadFactory(name + "-"), abortPolicyQueue, 0); pool.setCorePoolSize(core); abortPolicyQueue.pool = pool; return pool; } private static final class AbortPolicyQueue extends LinkedBlockingQueue implements RejectedExecutionHandler { private static final long serialVersionUID = 1L; private SumkExecutorService pool; private int softCapacity; AbortPolicyQueue(int capacity, int softCapacity) { super(capacity); this.softCapacity = softCapacity; } @Override public boolean offer(Runnable r) { if (this.size() > softCapacity) { return false; } return super.offer(r); } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (pool.isShutdown()) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString() + ",because of Thread pool shutdowned"); } if (PriorityRunnable.class == r.getClass() && ((PriorityRunnable) r).priority() < pool.threshold()) { String msg = new StringBuilder("Task ").append(r.toString()).append(" rejected from ") .append(e.toString()).append(", because of ").append(((PriorityRunnable) r).priority()) .append(" lower than ").append(pool.threshold()).toString(); throw new RejectedExecutionException(msg); } int waiting = this.size(); if (waiting % 50 == 0 && Log.get("sumk.thread").isWarnEnabled()) { Log.get("sumk.thread").warn("task is busy,waiting size:{}", waiting); } if (!super.offer(r)) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } } } private static class ThresholdThreadPool extends ThreadPoolExecutor implements SumkExecutorService { private int threshold; public ThresholdThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, int threshold) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); this.threshold = threshold; } @Override public int threshold() { return threshold; } @Override public void threshold(int threshold) { this.threshold = threshold; } @Override public void setThreadFactory(ThreadFactory threadFactory) { } @Override public void setCorePoolSize(int corePoolSize) { super.setCorePoolSize(corePoolSize); AbortPolicyQueue q = (AbortPolicyQueue) this.getQueue(); q.softCapacity = (int) (corePoolSize * 0.7); } @Override public int getQueued() { return this.getQueue().size(); } } } ================================================ FILE: sumk-base/src/main/java/org/yx/base/thread/ThresholdExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.base.thread; public interface ThresholdExecutor { int threshold(); void threshold(int threshold); } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/AbsoluteXmlFilesLoader.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.io.File; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.yx.util.FileUtil; public class AbsoluteXmlFilesLoader extends AbstractFilesLoader { public AbsoluteXmlFilesLoader(String rootUri, String subfix) { super(rootUri, subfix); } @Override public Map openResources(String db) throws Exception { List files = new ArrayList<>(); File root = new File(rootUri); if (!root.exists() || !root.isDirectory()) { return Collections.emptyMap(); } Map map = new HashMap<>(); FileUtil.listAllSubFiles(files, root); List timeList = new ArrayList<>(files.size()); for (int i = 0; i < files.size(); i++) { File f = files.get(i); if (!f.getName().endsWith(".xml")) { continue; } map.put(f.getName(), Files.readAllBytes(f.toPath())); timeList.add(new FileModifyTime(f.getAbsolutePath(), f.lastModified())); } this.times = timeList.toArray(new FileModifyTime[timeList.size()]); return map; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/AbstractFilesLoader.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.io.File; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import org.yx.util.Task; public abstract class AbstractFilesLoader implements MultiResourceLoader, Runnable { protected Consumer consumer; protected FileModifyTime[] times; protected final String rootUri; protected final String subfix; public AbstractFilesLoader(String rootUri, String subfix) { this.rootUri = Objects.requireNonNull(rootUri); this.subfix = "".equals(subfix) ? null : subfix; } @Override public synchronized boolean startListen(Consumer consumer) { if (this.consumer != null) { return false; } this.consumer = consumer; Task.scheduleAtFixedRate(this, 60, getRefreshDelay(), TimeUnit.SECONDS); return true; } protected long getRefreshDelay() { return AppInfo.getLong("sumk.file.refresh.delay", 60); } @Override public void run() { FileModifyTime[] times = this.times; if (times == null || times.length == 0) { return; } boolean modified = false; for (FileModifyTime ft : times) { File f = new File(ft.file); if (f.lastModified() > ft.lastModify) { modified = true; break; } } if (modified) { consumer.accept(this); } } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/AbstractRefreshableSystemConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.function.Consumer; import org.yx.base.StartOnceLifecycle; import org.yx.log.RawLog; public abstract class AbstractRefreshableSystemConfig extends StartOnceLifecycle implements RefreshableSystemConfig { protected static final String LOG_NAME = "sumk.conf"; protected Consumer observer; @Override public void setConsumer(Consumer observer) { this.observer = observer; } @Override public void onRefresh() { this.invokeConsumer(); AppInfo.notifyUpdate(); } protected final void invokeConsumer() { Consumer ob = this.observer; if (ob != null) { ob.accept(this); } } @Override protected final void onStart() { this.init(); RawLog.setLogger(RawLog.SLF4J_LOG); this.invokeConsumer(); } protected abstract void init(); } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/AppConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.yx.log.RawLog; import org.yx.util.CollectionUtil; import org.yx.util.IOUtil; import org.yx.util.Task; public class AppConfig extends AbstractRefreshableSystemConfig { protected final String fileName; protected final int periodTime; protected Map map = Collections.emptyMap(); protected boolean showLog = true; private boolean isFirst = true; protected ScheduledFuture future; public AppConfig() { this(System.getProperty("sumk.appinfo", "app.properties")); } public AppConfig(String fileName) { this(fileName, Integer.getInteger("sumk.appinfo.period", 1000 * 30)); } public AppConfig(String fileName, int periodTimeMS) { this.fileName = Objects.requireNonNull(fileName); this.periodTime = Math.max(periodTimeMS, 1000); } private InputStream openInputStream() throws FileNotFoundException { InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName); if (in != null) { return in; } File f = new File(fileName); if (f.exists()) { return new FileInputStream(f); } RawLog.info(LOG_NAME, "can not found " + this.fileName); return null; } private void handle() { try (InputStream in = openInputStream()) { if (in == null) { return; } byte[] bs = IOUtil.readAllBytes(in, true); Map conf = CollectionUtil.fillPropertiesFromText(new HashMap<>(), new String(bs, StandardCharsets.UTF_8)); if (conf != null && !conf.equals(this.map)) { if (this.showLog) { RawLog.info(LOG_NAME, fileName + " loaded"); } this.map = conf; if (!isFirst) { this.onRefresh(); } } this.isFirst = false; } catch (Exception e) { RawLog.error(LOG_NAME, e.getMessage(), e); } } public boolean isShowLog() { return showLog; } public void setShowLog(boolean showLog) { this.showLog = showLog; } @Override protected void init() { this.handle(); this.future = Task.scheduleAtFixedRate(this::handle, this.periodTime, this.periodTime, TimeUnit.MILLISECONDS); } @Override public Set keys() { return Collections.unmodifiableSet(this.map.keySet()); } @Override public synchronized void stop() { if (this.future != null) { this.future.cancel(false); } this.started = false; } @Override public String get(String key) { return map.get(key); } @Override public String toString() { return String.valueOf(map); } @Override public Map values() { return Collections.unmodifiableMap(map); } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/AppInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.lang.management.ManagementFactory; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import org.yx.base.context.AppContext; import org.yx.log.RawLog; import org.yx.util.StringUtil; public final class AppInfo { public static final String CLASSPATH_URL_PREFIX = "classpath:"; public static final String LN = Const.LN; private static final List> observers = new ArrayList<>(); public static final Charset UTF8 = StandardCharsets.UTF_8; private static SystemConfig info; private static String pid; static { init(); } private synchronized static void init() { try { String temp = ManagementFactory.getRuntimeMXBean().getName(); pid = temp.substring(0, temp.indexOf("@")); } catch (Throwable e) { RawLog.error("sumk.conf", e); pid = "UNKNOW"; } if (info != null) { return; } try { if (refreshConfig()) { return; } } catch (Exception e) { RawLog.error("sumk.conf", e); AppContext.startFailed(); } if (info == null) { try { setConfig(ComposedConfig.createSystemConfig(new AppConfig())); } catch (Exception e) { RawLog.error("sumk.conf", e); AppContext.startFailed(); } } } public static String pid() { return pid; } private static synchronized void setConfig(SystemConfig config) { if (info == null) { info = config; } config.start(); LocalhostUtil.setLocalIp(config.get("sumk.ip")); info = config; } static synchronized boolean refreshConfig() { SystemConfig config = SystemConfigHolder.config; if (info == config || config == null) { return false; } SystemConfig old = info; setConfig(config); notifyUpdate(); if (old != null) { old.stop(); } return true; } public static String getLocalIp() { return LocalhostUtil.getLocalIP(); } /** * @param defaultValue 如果没有设置的话,就返回这个默认值 * @return 当前应用的id */ public static String appId(String defaultValue) { return get("sumk.appId", defaultValue); } /** * @param name name * @return name不存在的话,返回null */ public static String get(String name) { return info.get(name); } public static String getLatin(String name) { String v = info.get(name); return v == null ? v : StringUtil.toLatin(v); } public static String getLatin(String name, String defaultValue) { String v = getLatin(name); return StringUtil.isEmpty(v) ? defaultValue : v; } public static String get(String name, String defaultValue) { String v = info.get(name); if (v == null || v.isEmpty()) { return defaultValue; } return v; } public static String get(String first, String second, String defaultValue) { String value = info.get(first); if (value != null && value.length() > 0) { return value; } return get(second, defaultValue); } public static int getInt(String name, int defaultValue) { String value = info.get(name); if (value == null || value.length() == 0) { return defaultValue; } try { return Integer.parseInt(value); } catch (Exception e) { return defaultValue; } } public static long getLong(String name, long defaultValue) { String value = info.get(name); if (value == null || value.length() == 0) { return defaultValue; } try { return Long.parseLong(value); } catch (Exception e) { return defaultValue; } } public static boolean getBoolean(String name, boolean defaultValue) { String value = info.get(name); if (value == null || value.length() == 0) { return defaultValue; } value = value.toLowerCase(); return "1".equals(value) || "true".equals(value); } public static Map subMap(String prefix) { if (prefix == null) { prefix = ""; } int len = prefix.length(); Map map = new HashMap<>(); for (String name : info.keys()) { if (name.startsWith(prefix)) { String v = info.get(name); if (v != null) { map.put(name.substring(len), v); } } } return map; } public static void addObserver(Consumer ob) { synchronized (observers) { if (observers.contains(ob)) { return; } observers.add(ob); } ob.accept(info); } public static void removeObserver(Consumer ob) { synchronized (observers) { observers.remove(ob); } } public static synchronized void notifyUpdate() { List> consumers; synchronized (observers) { consumers = new ArrayList<>(observers); } for (Consumer consumer : consumers) { try { consumer.accept(info); } catch (Throwable e) { RawLog.error("sumk.conf", e); } } } public static SystemConfig config() { return info; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/ClassPathXmlFilesLoader.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.io.File; import java.net.URL; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.yx.base.scaner.JarFileUtil; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.util.FileUtil; import org.yx.util.Loader; public class ClassPathXmlFilesLoader extends AbstractFilesLoader { public ClassPathXmlFilesLoader(String rootUri, String subfix) { super(rootUri, subfix); } @Override public Map openResources(String db) throws Exception { Enumeration urls = Loader.getResources(rootUri); if (urls == null || !urls.hasMoreElements()) { if (AppInfo.getBoolean("sumk.conf.classpath.force", false)) { throw new SumkException(356453425, "can not find path " + rootUri); } return Collections.emptyMap(); } final Map map = new HashMap<>(); Collection files = new HashSet<>(); Logger log = Log.get("sumk.conf"); do { URL url = urls.nextElement(); if (JarFileUtil.URL_PROTOCOL_JAR.equals(url.getProtocol()) || JarFileUtil.URL_PROTOCOL_ZIP.equals(url.getProtocol())) { Map tempMap = subfix == null ? JarFileUtil.exactResourcesInJar(url, null) : JarFileUtil.exactResourcesInJar(url, node -> node.endsWith(subfix)); if (log.isDebugEnabled()) { log.debug("url:{},size:{}", url, tempMap == null ? 0 : tempMap.size()); } if (tempMap != null && tempMap.size() > 0) { map.putAll(tempMap); } continue; } File f = new File(url.toURI()); if (f.exists() && f.isDirectory()) { FileUtil.listAllSubFiles(files, f); } } while (urls.hasMoreElements()); if (files.size() > 0) { List timeList = new ArrayList<>(files.size()); for (File f : files) { String path = f.getAbsolutePath(); if (subfix != null && !path.endsWith(subfix)) { continue; } map.put(path, Files.readAllBytes(f.toPath())); timeList.add(new FileModifyTime(path, f.lastModified())); } this.times = timeList.toArray(new FileModifyTime[timeList.size()]); } else { this.times = null; } if (log.isDebugEnabled()) { FileModifyTime[] ts = this.times; Map timeMap = new HashMap<>(); if (ts != null && ts.length > 0) { for (FileModifyTime t : ts) { timeMap.put(t.file, t.lastModify); } } log.debug("rootUri:{},times size:{},map size:{}", this.rootUri, timeMap.size(), map.size()); for (String p : map.keySet()) { StringBuilder sb = new StringBuilder(); sb.append(p).append(':').append(map.get(p).length); Long lastModify = timeMap.get(p); if (lastModify != null) { sb.append(" - ").append(lastModify); } log.debug(sb.toString()); } } return map; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/ComposedConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import org.yx.base.matcher.Matchers; import org.yx.log.RawLog; import org.yx.util.CollectionUtil; public class ComposedConfig implements SystemConfig, Consumer { /** * 优先级最高的配置,是不可变的map,且不为null */ private Map top; /** * 优先级最低的配置,是不可变的map,且不为null */ private Map lowest; private final RefreshableSystemConfig config; private Map composedMap = Collections.emptyMap(); public ComposedConfig(Map top, RefreshableSystemConfig config) { this(top, config, null); } public ComposedConfig(Map top, RefreshableSystemConfig config, Map lowest) { this.top = CollectionUtil.unmodifyMap(top); this.lowest = CollectionUtil.unmodifyMap(lowest); this.config = Objects.requireNonNull(config); this.config.setConsumer(this); } public static ComposedConfig createSystemConfig(RefreshableSystemConfig conf) { Predicate exclude = Matchers.createWildcardMatcher( "java.*,sun.*,jdk.*,awt.toolkit,file.encoding,file.encoding.pkg,file.separator,line.separator,os.arch,os.name,os.version,path.separator,user.country,user.dir,user.home,user.language,user.name,user.script,user.timezone,user.variant", 1); Map map = Collections.emptyMap(); for (int i = 0; i < 1000; i++) { try { Map tmp = new HashMap<>(); Properties p = System.getProperties(); for (Entry en : p.entrySet()) { Object k = en.getKey(); Object v = en.getValue(); if (k != null && k.getClass() == String.class && v != null && v.getClass() == String.class) { String key = (String) k; if (exclude.test(key)) { continue; } tmp.put(key, (String) v); } } map = tmp; } catch (Exception e) { RawLog.error("sumk.conf", "iterate system properties error," + e); } } return new ComposedConfig(map, conf); } @Override public void accept(RefreshableSystemConfig t) { Map tmp = new HashMap<>(this.lowest); tmp.putAll(t.values()); tmp.putAll(this.top); this.composedMap = tmp; } @Override public void start() { this.config.start(); } @Override public void stop() { this.config.stop(); } @Override public String get(String key) { return this.composedMap.get(key); } @Override public Set keys() { return Collections.unmodifiableSet(this.composedMap.keySet()); } public Map topConfig() { return this.top; } public Map lowConfig() { return this.lowest; } protected void topConfig(Map top) { this.top = CollectionUtil.unmodifyMap(top); } protected void lowConfig(Map low) { this.lowest = CollectionUtil.unmodifyMap(low); } public int size() { return this.composedMap.size(); } public RefreshableSystemConfig innerConfig() { return this.config; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/Const.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; public final class Const { public static final String DEFAULT_DB_NAME = "sumk"; public static final int SUMK_VERSION = 0x421; public static String sumkVersion() { return new StringBuilder(10).append((Const.SUMK_VERSION >> 8) & 0x0F).append('.') .append((Const.SUMK_VERSION >> 4) & 0x0F).append('.').append(Const.SUMK_VERSION & 0x0F).toString(); } public static final String KEY_STORE_PATH = "sumk.webserver.ssl.keyStore"; /** * 分号 ; */ public static final String SEMICOLON = ";"; /** * 逗号 , */ public static final String COMMA = ","; public static final String LN = "\n"; public static final int DEFAULT_TOPLIMIT = 50000; /** * 这个监听程序不要直接操作数据库 */ public static final String LISTENER_DB_MODIFY = "LISTENER_DB_MODIFY"; /** * 这个监听程序不要直接操作数据库 */ public static final String LISTENER_DB_QUERY = "LISTENER_DB_QUERY"; /** * 提交前监听,这个监听程序可以操作数据库,它的插入事件也可以被LISTENER_DB_MODIFY监听器监听到。
* 注意:它不能监听TransactionType为AUTO_COMMIT的数据库操作 */ public static final String LISTENER_DB_MODIFY_ON_COMMIT = "LISTENER_DB_MODIFY_ON_COMMIT"; } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/FileModifyTime.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; public class FileModifyTime { final String file; long lastModify; public FileModifyTime(String file, long lastModify) { this.file = file; this.lastModify = lastModify; } @Override public String toString() { return file + ":" + lastModify; } public long getLastModify() { return lastModify; } public void setLastModify(long lastModify) { this.lastModify = lastModify; } public String getFile() { return file; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/LocalMultiResourceLoaderSupplier.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.Objects; import java.util.function.Supplier; public class LocalMultiResourceLoaderSupplier implements Supplier { private final String rootUri; public LocalMultiResourceLoaderSupplier(String uri) { this.rootUri = Objects.requireNonNull(uri); } @Override public MultiResourceLoader get() { String xml = ".xml"; if (rootUri.startsWith(AppInfo.CLASSPATH_URL_PREFIX)) { return new ClassPathXmlFilesLoader(rootUri.substring(AppInfo.CLASSPATH_URL_PREFIX.length()), xml); } return new AbsoluteXmlFilesLoader(rootUri, xml); } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/LocalhostUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.log.RawLog; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public final class LocalhostUtil { private static String localIp = null; private static String[] localIps = null; private static Predicate matcher = defaultMatcher(); private static Predicate defaultMatcher() { Predicate m = Matchers.createWildcardMatcher("10.*,172.*,16.*,192.*,168.*", 1); Predicate not1 = Matchers.createWildcardMatcher("*.1", 1).negate(); return m.and(not1); } private static boolean isValid(InetAddress ia) { if (ia instanceof Inet6Address && AppInfo.getBoolean("sumk.local.ipv6.disable", false)) { return false; } return !ia.isAnyLocalAddress() && !ia.isLoopbackAddress(); } private static String getHostAddress(InetAddress ia) { String address = ia.getHostAddress(); int index = address.indexOf('%'); if (index > 0 && AppInfo.getBoolean("sumk.ipv6.pure", true)) { address = address.substring(0, index); } return address; } public static synchronized boolean setLocalIp(String ip) { if (ip == null) { ip = ""; } try { ip = StringUtil.toLatin(ip).trim(); if (ip.contains(Matchers.WILDCARD) || ip.contains(",")) { matcher = Matchers.createWildcardMatcher(ip, 1); resetLocalIp(); return true; } matcher = defaultMatcher(); if (ip.length() > 0) { localIp = ip; } else { resetLocalIp(); } return true; } catch (Exception e) { RawLog.error("sumk.conf", e.getMessage(), e); return false; } } public static String getLocalIP() { if (localIp != null) {// localIp不会从非null变为null return localIp; } resetLocalIp(); return localIp; } private synchronized static void resetLocalIp() { try { String ip = getLocalIP0(); if (ip != null && ip.length() > 0) { localIp = ip; return; } } catch (Exception e) { RawLog.error("sumk.conf", e.getMessage(), e); } if (localIp == null) { localIp = "0.0.0.0"; } } private static String getLocalIP0() throws Exception { String[] ips = getLocalIps(); Predicate matcher = LocalhostUtil.matcher; if (matcher == null) { matcher = BooleanMatcher.TRUE; } for (String ip : ips) { if (matcher.test(ip)) { return ip; } } if (ips.length > 0) { RawLog.warn("sumk.conf", "没有合适ip,使用第一个ip,列表为:" + Arrays.toString(ips)); return ips[0]; } RawLog.warn("sumk.conf", "找不到任何ip,使用0.0.0.0"); return "0.0.0.0"; } private static synchronized String[] getLocalIps() { if (localIps != null) { return localIps; } List ipList = new ArrayList(); try { Enumeration e1 = (Enumeration) NetworkInterface.getNetworkInterfaces(); while (e1.hasMoreElements()) { NetworkInterface ni = (NetworkInterface) e1.nextElement(); if (!isUp(ni)) { continue; } Enumeration e2 = ni.getInetAddresses(); while (e2.hasMoreElements()) { InetAddress ia = (InetAddress) e2.nextElement(); if (isValid(ia)) { ipList.add(getHostAddress(ia)); } } } } catch (SocketException e) { RawLog.error("sumk.conf", e.getMessage(), e); } if (ipList.isEmpty()) { return new String[0]; } localIps = ipList.toArray(new String[ipList.size()]); return localIps; } /** * 网卡是否有效 * * @param ni * @return * @throws SocketException */ private static boolean isUp(NetworkInterface ni) throws SocketException { return (!ni.isVirtual()) && ni.isUp() && (!ni.isLoopback()); } public static List getLocalIPList() { return CollectionUtil.unmodifyList(getLocalIps()); } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/MapConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class MapConfig implements SystemConfig { private Map map = new ConcurrentHashMap<>(); public static MapConfig create() { return new MapConfig(); } public Map map() { return map; } public MapConfig replace(Map config) { map = Objects.requireNonNull(config); return this; } @Override public String get(String key) { return map.get(key); } @Override public Set keys() { return new HashSet<>(map.keySet()); } public MapConfig put(String k, String v) { map.put(k, v); return this; } public String remove(String key) { return this.map.remove(key); } public MapConfig putKV(String kv) { map.put(kv.split("=")[0].trim(), kv.split("=")[1].trim()); return this; } @Override public void start() { } @Override public void stop() { } @Override public String toString() { return "MapConfig: " + map; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/MultiNodeConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import static org.yx.conf.AppInfo.LN; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import org.yx.util.CollectionUtil; public abstract class MultiNodeConfig extends AbstractRefreshableSystemConfig { protected Map config = Collections.emptyMap(); private Function> dataParser = data -> { String s = new String(data, AppInfo.UTF8).trim().replace("\r\n", LN).replace("\r", LN); return CollectionUtil.fillPropertiesFromText(new HashMap<>(), s); }; public Function> getDataParser() { return dataParser; } public void setDataParser(Function> dataParser) { this.dataParser = Objects.requireNonNull(dataParser); } protected Map parse(byte[] data) { return this.dataParser.apply(data); } @Override public String get(String key) { return config.get(key); } @Override public Set keys() { return config.keySet(); } @Override public Map values() { return Collections.unmodifiableMap(this.config); } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/MultiResourceLoader.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.Map; import java.util.function.Consumer; public interface MultiResourceLoader { Map openResources(String name) throws Exception; boolean startListen(Consumer consumer); } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/RefreshableSystemConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.Map; import java.util.function.Consumer; public interface RefreshableSystemConfig extends SystemConfig { void setConsumer(Consumer observer); void onRefresh(); Map values(); } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/SimpleBeanUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; import org.yx.log.Log; import org.yx.util.StringUtil; public class SimpleBeanUtil { public static void setProperty(Object bean, Method m, String value) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (value == null) { Log.get("sumk.bean").debug("{} was ignored because value is null", m.getName()); return; } value = value.trim(); if (value.isEmpty()) { Log.get("sumk.bean").debug("{} was ignored because value is empty", m.getName()); return; } Class ptype = m.getParameterTypes()[0]; Object v = parseValue(ptype, value); if (v == null) { Log.get("sumk.bean").warn("{}因为类型({})不支持,被过滤掉", m.getName(), ptype); return; } m.invoke(bean, v); } public static Object parseValue(Class ptype, String value) { if (ptype == String.class) { return value; } if (ptype == int.class || ptype == Integer.class) { return Integer.valueOf(value); } if (ptype == long.class || ptype == Long.class) { return Long.valueOf(value); } if (ptype == double.class || ptype == Double.class) { return Double.valueOf(value); } if (ptype == boolean.class || ptype == Boolean.class) { if ("1".equals(value) || "true".equalsIgnoreCase(value)) { return Boolean.TRUE; } return Boolean.FALSE; } if (ptype == short.class || ptype == Short.class) { return Short.valueOf(value); } if (ptype == byte.class || ptype == Byte.class) { return Byte.valueOf(value); } if (ptype == float.class || ptype == Float.class) { return Float.valueOf(value); } if (ptype == char.class || ptype == Character.class) { if (value.length() > 0) { return value.charAt(0); } } return null; } public static void copyProperties(Object bean, Map map) throws Exception { Set set = map.keySet(); Method[] ms = bean.getClass().getMethods(); for (String key : set) { Method m = getMethod(ms, key); if (m == null) { Log.get("sumk.bean").warn("{}在{}中不存在", key, bean.getClass().getSimpleName()); continue; } setProperty(bean, m, map.get(key)); } } private static Method getMethod(Method[] ms, String key) { String methodName = "set" + StringUtil.capitalize(key); for (Method m : ms) { if (m.getName().equals(methodName) && m.getParameterCount() == 1) { return m; } } return null; } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/SystemConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.Set; import org.yx.base.Lifecycle; public interface SystemConfig extends Lifecycle { String get(String key); Set keys(); } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/SystemConfigHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.util.Objects; public class SystemConfigHolder { static SystemConfig config; /** * 通过外部方式注入,这种方式不一定会调用init方法 * * @param config 外部注入 * @return true表示设置被更新,如果config跟原来是同一个对象,也会返回false */ public static synchronized boolean setSystemConfig(SystemConfig config) { SystemConfigHolder.config = Objects.requireNonNull(config); return AppInfo.refreshConfig(); } } ================================================ FILE: sumk-base/src/main/java/org/yx/conf/UrlSystemConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.conf; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.yx.exception.SumkException; import org.yx.log.RawLog; import org.yx.util.CollectionUtil; import org.yx.util.IOUtil; import org.yx.util.Task; public class UrlSystemConfig extends MultiNodeConfig { protected final List urls; protected long period = 1000L * 60 * 5; protected int timeout = 5000; protected int readTimeout = 5000; protected String method = "GET"; private Future future; public UrlSystemConfig(List urls) { if (urls == null || urls.isEmpty()) { throw new SumkException(345212465, "url config的地址为空"); } this.urls = CollectionUtil.unmodifyList(urls); } public final synchronized void stop() { this.started = false; this.onStop(); } protected void onStop() { Future f = this.future; if (f != null) { f.cancel(true); this.future = null; } } /** * 初始化 */ @Override public void init() { if (this.future != null) { return; } this.future = Task.scheduleAtFixedRate(this::handle, this.period, this.period, TimeUnit.MILLISECONDS); } protected void handle() { Map map = new HashMap<>(); List list = new ArrayList<>(urls); Collections.reverse(list); for (URL url : list) { byte[] data = this.extractData(url); if (data == null) { RawLog.error(LOG_NAME, "data on [" + url + "] is null"); return; } if (data.length > 0) { map.putAll(this.parse(data)); } } if (Objects.equals(this.config, map)) { return; } RawLog.info(LOG_NAME, "data changed"); this.config = map; this.onRefresh(); } protected byte[] extractData(URL url) { HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(timeout); conn.setRequestMethod(method); conn.setReadTimeout(readTimeout); conn.setDoOutput(true); conn.connect(); if (conn.getResponseCode() != 200) { RawLog.error(LOG_NAME, url + "返回的状态码是" + conn.getResponseCode()); return null; } InputStream in = conn.getInputStream(); return IOUtil.readAllBytes(in, true); } catch (Exception e) { RawLog.error(LOG_NAME, e); return null; } finally { if (conn != null) { try { conn.disconnect(); } catch (Exception e2) { } } } } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public int getReadTimeout() { return readTimeout; } public void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public long getPeriod() { return period; } public void setPeriod(long period) { if (this.future != null) { throw new SumkException(62654622, "启动后不允许修改刷新间隔"); } this.period = period; } } ================================================ FILE: sumk-base/src/main/java/org/yx/exception/BizException.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.exception; import org.yx.conf.AppInfo; /** * 它的code最好要大于0,并且避开3位数,因为内置的错误码都是3位数 */ public class BizException extends CodeException { private static final long serialVersionUID = 453453454L; public BizException(int code, String msg) { super(String.valueOf(code), msg); } public BizException(String code, String msg) { super(code, msg); } public static BizException create(int code, String msg) { return new BizException(code, msg); } public static void throwException(int code, String msg) throws BizException { throw new BizException(code, msg); } public static BizException create(String code, String msg) { return new BizException(code, msg); } public static void throwException(String code, String msg) throws BizException { throw new BizException(code, msg); } @Override public Throwable fillInStackTrace() { if (AppInfo.getBoolean("sumk.bizexception.fullstack", true)) { return super.fillInStackTrace(); } return this; } } ================================================ FILE: sumk-base/src/main/java/org/yx/exception/CodeException.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.exception; import java.util.Objects; public abstract class CodeException extends RuntimeException { private static final long serialVersionUID = 1L; protected final String code; public CodeException(String code, String msg) { super(msg); this.code = Objects.requireNonNull(code); } public CodeException(String code, String msg, Throwable exception) { super(msg, exception); this.code = Objects.requireNonNull(code); } public String getCode() { return code; } public boolean isSameCode(String expect) { return this.code.equals(expect); } @Override public String getLocalizedMessage() { return new StringBuilder().append(this.getMessage()).append(" [").append(code).append(']').toString(); } @Override public final String toString() { return new StringBuilder(this.getClass().getSimpleName()).append(": ").append(this.getLocalizedMessage()) .toString(); } } ================================================ FILE: sumk-base/src/main/java/org/yx/exception/SimpleSumkException.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.exception; public final class SimpleSumkException extends SumkException { private static final long serialVersionUID = 1L; public SimpleSumkException(int code, String msg) { super(code, msg); } @Override public Throwable fillInStackTrace() { return this; } } ================================================ FILE: sumk-base/src/main/java/org/yx/exception/SoaException.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.exception; import java.io.PrintStream; import java.io.PrintWriter; import org.yx.conf.AppInfo; import org.yx.util.ExceptionUtil; public final class SoaException extends CodeException { private static final long serialVersionUID = 453453343454L; private String detailError; private String exceptionClz; private boolean bizException; public String getDetailError() { return this.detailError; } public String getExceptionClz() { return exceptionClz; } public static SoaException create(String code, String msg, Throwable cause) { String detailError = getException(cause); SoaException ex; if (cause instanceof BizException) { BizException bizEx = (BizException) cause; ex = new SoaException(bizEx.getCode(), bizEx.getMessage(), detailError); ex.bizException = true; } else { ex = new SoaException(code, msg, detailError); } ex.exceptionClz = cause == null ? null : cause.getClass().getName(); return ex; } public SoaException(String code, String msg, String detail) { super(code, msg); this.exceptionClz = null; this.detailError = detail; } private static String getException(Throwable e) { if (e == null) { return null; } if (AppInfo.getBoolean("sumk.rpc.detailError", false)) { StringBuilder sb = new StringBuilder(64); ExceptionUtil.printStackTrace(sb, e); if (sb.length() >= 4000) { return sb.substring(0, 4000); } return sb.toString(); } return e.getMessage(); } private boolean printRawStackTrace() { return AppInfo.getBoolean("sumk.rpc.printRawStackTrace", false); } @Override public Throwable fillInStackTrace() { if (printRawStackTrace()) { super.fillInStackTrace(); } return this; } private String buildStackTraceMessage() { StringBuilder sb = new StringBuilder().append(this.toString()); if (this.exceptionClz != null && this.exceptionClz.length() > 0) { sb.append("\n\tcause by " + this.exceptionClz); if (this.detailError != null && this.detailError.length() > 0) { sb.append(":").append(this.detailError); } } return sb.toString(); } @Override public void printStackTrace(PrintStream s) { if (printRawStackTrace()) { super.printStackTrace(s); return; } s.println(this.buildStackTraceMessage()); } @Override public void printStackTrace(PrintWriter s) { if (printRawStackTrace()) { super.printStackTrace(s); return; } s.println(this.buildStackTraceMessage()); } public boolean isBizException() { return bizException; } } ================================================ FILE: sumk-base/src/main/java/org/yx/exception/SumkException.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.exception; /** * 这个异常表示是执行sumk框架中的某个方法抛出的异常,它的code最大为0
* 框架内部使用,业务异常请用BizException * */ public class SumkException extends CodeException { private static final long serialVersionUID = 3453246435546L; public SumkException(int code, String msg) { super(stringCode(code), msg); } public SumkException(int code, String msg, Throwable exception) { super(stringCode(code), msg, exception); } private static String stringCode(int code) { return String.valueOf(code); } public static SumkException wrap(Throwable e) { if (e instanceof SumkException) { throw (SumkException) e; } return new SumkException(34534565, e.getMessage(), e); } } ================================================ FILE: sumk-base/src/main/java/org/yx/exception/SumkExceptionCode.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.exception; /** * 这里的code是不可变的,可以用它对SumkException中的code做判断。 它们都是-91275XXXX格式 */ public interface SumkExceptionCode { /** * 存在多个bean */ int TOO_MANY_BEAN = 912753951; /** * 数据库连接已经关闭 */ int DB_CONNECTION_CLOSED = 912753820; /** * 数据库queryOne()的时候,返回的结果不止一条 */ int DB_TOO_MANY_RESULTS = 912753811; /** * redis连接异常 */ int REDIS_DIS_CONNECTION = 912753701; } ================================================ FILE: sumk-base/src/main/java/org/yx/log/CodeLineMarker.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import java.util.Iterator; import java.util.NoSuchElementException; import org.slf4j.Marker; public class CodeLineMarker implements Marker { private static final long serialVersionUID = 1L; private final String packageName; public CodeLineMarker(String packageName) { this.packageName = packageName; } @Override public String getName() { return packageName; } @Override public void add(Marker reference) { } @Override public boolean remove(Marker reference) { return false; } @Override public boolean hasChildren() { return false; } @Override public boolean hasReferences() { return false; } @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return false; } @Override public Marker next() { throw new NoSuchElementException(); } }; } @Override public boolean contains(Marker other) { return false; } @Override public boolean contains(String name) { return false; } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/ConsoleLog.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import org.slf4j.Marker; import org.yx.util.SumkDate; public class ConsoleLog extends SumkLogger { private static final Loggers loggers = Loggers.create("ConsoleLog"); public static final SumkLogger defaultLog = ConsoleLog.get("sumk.log"); public static SumkLogger get(String name) { SumkLogger log = loggers.get(name); if (log != null) { return log; } log = new ConsoleLog(name); SumkLogger oldInstance = loggers.putIfAbsent(name, log); return oldInstance == null ? log : oldInstance; } private ConsoleLog(String module) { super(module); } @Override protected void output(Marker marker, LogLevel methodLevel, String format, Object... arguments) { this.show(methodLevel, format, arguments); } @Override protected void output(Marker marker, LogLevel methodLevel, String msg, Throwable e) { StringBuilder sb = new StringBuilder(128); sb.append(currentTime()).append(" [").append(Thread.currentThread().getName()).append("] ").append(methodLevel) .append(" ").append(LogKits.shorterPrefix(name, 40)).append(" - ").append(msg).append("\n"); System.err.print(sb.toString()); e.printStackTrace(); } private void show(LogLevel level, String msg, Object... args) { msg = LogKits.buildMessage(msg, args); StringBuilder sb = new StringBuilder(128); sb.append(currentTime()).append(" ["); sb.append(Thread.currentThread().getName()).append("] ").append(level).append(" ") .append(LogKits.shorterPrefix(name, 40)).append(" - ").append(msg); System.out.println(sb.toString()); } private StringBuilder currentTime() { SumkDate d = SumkDate.now(); StringBuilder sb = new StringBuilder(26); sb.append(d.to_yyyy_MM_dd()).append('T').append(d.to_HH_mm_ss_SSS()); return sb; } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/DelegateLogger.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; public final class DelegateLogger implements Logger { private static int count = 0; private static void incrCount() { count++; if (count < 0) { count = 0; } } private Logger delegate; private DelegateLogger(String name) { this.delegate = ConsoleLog.get(name); } public static DelegateLogger get(String name) { return new DelegateLogger(name); } public Logger delegate() { if (delegate.getClass() != ConsoleLog.class) { return delegate; } incrCount(); if (count % 20 != 10) { return delegate; } try { Logger slfLog = LoggerFactory.getLogger(delegate.getName()); if (slfLog == null) { return delegate; } if (slfLog instanceof ConsoleLog || slfLog instanceof DelegateLogger) { return delegate; } if (Log.isNOPLogger(slfLog)) { return delegate; } delegate = slfLog; ConsoleLog.defaultLog.info("{} change to {}", delegate.getName(), delegate.getClass().getSimpleName()); } catch (Throwable e) { ConsoleLog.defaultLog.warn("can not change to slf4j. throwable {}:{}", e.getClass().getName(), e.getMessage()); } return delegate; } @Override public String getName() { return delegate().getName(); } @Override public boolean isTraceEnabled() { return delegate().isTraceEnabled(); } @Override public void trace(String msg) { delegate().trace(msg); } @Override public void trace(String format, Object arg) { delegate().trace(format, arg); } @Override public void trace(String format, Object arg1, Object arg2) { delegate().trace(format, arg1, arg2); } @Override public void trace(String format, Object... arguments) { delegate().trace(format, arguments); } @Override public void trace(String msg, Throwable t) { delegate().trace(msg, t); } @Override public boolean isTraceEnabled(Marker marker) { return delegate().isTraceEnabled(marker); } @Override public void trace(Marker marker, String msg) { delegate().trace(marker, msg); } @Override public void trace(Marker marker, String format, Object arg) { delegate().trace(marker, format, arg); } @Override public void trace(Marker marker, String format, Object arg1, Object arg2) { delegate().trace(marker, format, arg1, arg2); } @Override public void trace(Marker marker, String format, Object... arguments) { delegate().trace(marker, format, arguments); } @Override public void trace(Marker marker, String msg, Throwable t) { delegate().trace(marker, msg, t); } @Override public boolean isDebugEnabled() { return delegate().isDebugEnabled(); } @Override public void debug(String msg) { delegate().debug(msg); } @Override public void debug(String format, Object arg) { delegate().debug(format, arg); } @Override public void debug(String format, Object arg1, Object arg2) { delegate().debug(format, arg1, arg2); } @Override public void debug(String format, Object... arguments) { delegate().debug(format, arguments); } @Override public void debug(String msg, Throwable t) { delegate().debug(msg, t); } @Override public boolean isDebugEnabled(Marker marker) { return delegate().isDebugEnabled(marker); } @Override public void debug(Marker marker, String msg) { delegate().debug(marker, msg); } @Override public void debug(Marker marker, String format, Object arg) { delegate().debug(marker, format, arg); } @Override public void debug(Marker marker, String format, Object arg1, Object arg2) { delegate().debug(marker, format, arg1, arg2); } @Override public void debug(Marker marker, String format, Object... arguments) { delegate().debug(marker, format, arguments); } @Override public void debug(Marker marker, String msg, Throwable t) { delegate().debug(marker, msg, t); } @Override public boolean isInfoEnabled() { return delegate().isInfoEnabled(); } @Override public void info(String msg) { delegate().info(msg); } @Override public void info(String format, Object arg) { delegate().info(format, arg); } @Override public void info(String format, Object arg1, Object arg2) { delegate().info(format, arg1, arg2); } @Override public void info(String format, Object... arguments) { delegate().info(format, arguments); } @Override public void info(String msg, Throwable t) { delegate().info(msg, t); } @Override public boolean isInfoEnabled(Marker marker) { return delegate().isInfoEnabled(marker); } @Override public void info(Marker marker, String msg) { delegate().info(marker, msg); } @Override public void info(Marker marker, String format, Object arg) { delegate().info(marker, format, arg); } @Override public void info(Marker marker, String format, Object arg1, Object arg2) { delegate().info(marker, format, arg1, arg2); } @Override public void info(Marker marker, String format, Object... arguments) { delegate().info(marker, format, arguments); } @Override public void info(Marker marker, String msg, Throwable t) { delegate().info(marker, msg, t); } @Override public boolean isWarnEnabled() { return delegate().isWarnEnabled(); } @Override public void warn(String msg) { delegate().warn(msg); } @Override public void warn(String format, Object arg) { delegate().warn(format, arg); } @Override public void warn(String format, Object arg1, Object arg2) { delegate().warn(format, arg1, arg2); } @Override public void warn(String format, Object... arguments) { delegate().warn(format, arguments); } @Override public void warn(String msg, Throwable t) { delegate().warn(msg, t); } @Override public boolean isWarnEnabled(Marker marker) { return delegate().isWarnEnabled(marker); } @Override public void warn(Marker marker, String msg) { delegate().warn(marker, msg); } @Override public void warn(Marker marker, String format, Object arg) { delegate().warn(marker, format, arg); } @Override public void warn(Marker marker, String format, Object arg1, Object arg2) { delegate().warn(marker, format, arg1, arg2); } @Override public void warn(Marker marker, String format, Object... arguments) { delegate().warn(marker, format, arguments); } @Override public void warn(Marker marker, String msg, Throwable t) { delegate().warn(marker, msg, t); } @Override public boolean isErrorEnabled() { return delegate().isErrorEnabled(); } @Override public void error(String msg) { delegate().error(msg); } @Override public void error(String format, Object arg) { delegate().error(format, arg); } @Override public void error(String format, Object arg1, Object arg2) { delegate().error(format, arg1, arg2); } @Override public void error(String format, Object... arguments) { delegate().error(format, arguments); } @Override public void error(String msg, Throwable t) { delegate().error(msg, t); } @Override public boolean isErrorEnabled(Marker marker) { return delegate().isErrorEnabled(marker); } @Override public void error(Marker marker, String msg) { delegate().error(marker, msg); } @Override public void error(Marker marker, String format, Object arg) { delegate().error(marker, format, arg); } @Override public void error(Marker marker, String format, Object arg1, Object arg2) { delegate().error(marker, format, arg1, arg2); } @Override public void error(Marker marker, String format, Object... arguments) { delegate().error(marker, format, arguments); } @Override public void error(Marker marker, String msg, Throwable t) { delegate().error(marker, msg, t); } public void setDelegate(Logger delegate) { this.delegate = Objects.requireNonNull(delegate); } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/Log.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class Log { private static final String SUMKBOX = ".sumkbox."; private Log() { } public static boolean isTraceEnable(String module) { return get(module).isTraceEnabled(); } public static Logger get(Class clz) { String name = clz.getName(); if (name.contains(SUMKBOX)) { int index = name.lastIndexOf(SUMKBOX); name = String.join(".", name.substring(0, index), name.substring(index + SUMKBOX.length())); } return get(name); } public static Logger get(String module) { if (module == null) { module = ""; } Logger logger = LoggerFactory.getLogger(module); if (isNOPLogger(logger)) { return DelegateLogger.get(module); } return logger; } public static boolean isNOPLogger(Logger logger) { return "NOPLogger".equals(logger.getClass().getSimpleName()); } public static void printStack(String module, Throwable e) { get(module).error(e.getLocalizedMessage(), e); } public static boolean isON(Logger log) { return log.isTraceEnabled() && log instanceof SumkLogger && ((SumkLogger) log).isON(); } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/LogKits.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import org.slf4j.spi.LocationAwareLogger; public final class LogKits { private static final String DELIM_STR = "{}"; public static String shorterPrefix(String name, int maxLogNameLength) { if (maxLogNameLength < 5 || name == null || name.length() <= maxLogNameLength) { return name; } return "..".concat(name.substring(name.length() - maxLogNameLength + 2)); } public static String shorterSubfix(String text, int maxLogNameLength) { if (maxLogNameLength < 5 || text == null || text.length() <= maxLogNameLength) { return text; } return text.substring(0, maxLogNameLength - 2).concat(".."); } public static String buildMessage(String msg, Object... args) { if (msg == null || args == null || args.length == 0 || !msg.contains(DELIM_STR)) { return msg; } StringBuilder sb = new StringBuilder(msg.length() + 50); int argIndex = 0; int start = 0; int index = 0; while (argIndex < args.length && (index = msg.indexOf(DELIM_STR, start)) >= 0) { int escapeCount = 0; for (int i = index - 1; i >= start; i--) { if (msg.charAt(i) != '\\') { break; } escapeCount++; } sb.append(msg.substring(start, index)); if (escapeCount % 2 == 0) { sb.append(String.valueOf(args[argIndex])); argIndex++; } else { sb.append(DELIM_STR); } start = index + 2; } if (start < msg.length()) { sb.append(msg.substring(start)); } return sb.toString(); } public static LogLevel fromSlf4jLocationAwareLoggerInt(int logger_int) { if (logger_int >= LocationAwareLogger.ERROR_INT) { return LogLevel.ERROR; } if (logger_int >= LocationAwareLogger.WARN_INT) { return LogLevel.WARN; } if (logger_int >= LocationAwareLogger.INFO_INT) { return LogLevel.INFO; } if (logger_int >= LocationAwareLogger.DEBUG_INT) { return LogLevel.DEBUG; } return LogLevel.TRACE; } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/LogLevel.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; public enum LogLevel { ON, TRACE, DEBUG, INFO, WARN, ERROR, OFF; } ================================================ FILE: sumk-base/src/main/java/org/yx/log/LogSettings.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import org.yx.conf.AppInfo; public final class LogSettings { private static final int DEFAULT_MAX_BODY_LENGTH = 1500; private static final int DEFAULT_MAX_LOG_NAME_LENGTH = 32; private static boolean console; private static boolean showAttach; private static int maxBodyLength = DEFAULT_MAX_BODY_LENGTH; private static int maxLogNameLength = DEFAULT_MAX_LOG_NAME_LENGTH; public static void updateSettings() { console = AppInfo.getBoolean("sumk.log.console", false); showAttach = AppInfo.getBoolean("sumk.log.attach.show", true); maxBodyLength = AppInfo.getInt("sumk.log.body.maxlength", DEFAULT_MAX_BODY_LENGTH); maxLogNameLength = AppInfo.getInt("sumk.log.maxLogNameLength", DEFAULT_MAX_LOG_NAME_LENGTH); } public static boolean showAttach() { return showAttach; } public static boolean consoleEnable() { return console; } public static int maxBodyLength() { return maxBodyLength; } public static int maxLogNameLength() { return maxLogNameLength; } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/Loggers.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.conf.SystemConfig; import org.yx.util.StringUtil; public final class Loggers { private static final String ROOT = ""; private static LogLevel DEFAULT_LEVEL = LogLevel.INFO; private static ConcurrentMap _levelMap = new ConcurrentHashMap<>(); private ConcurrentMap map = new ConcurrentHashMap<>(); private final String name; private Loggers(String name) { this.name = name; } @Override public String toString() { return "Loggers [name=" + name + "]"; } private static final Consumer observer = info -> { try { Map newLevels = new HashMap<>(); String temp = AppInfo.getLatin("sumk.log.level", null); if (temp == null) { Loggers.resetLevel(newLevels); return; } List levelStrs = StringUtil.splitAndTrim(temp, Const.COMMA, Const.SEMICOLON, "\r", Const.LN); for (String levelStr : levelStrs) { String[] levelNamePair = levelStr.split(":"); switch (levelNamePair.length) { case 1: String level = levelNamePair[0].trim().toUpperCase(); if (level.contains(".")) { System.err.println("[" + levelStr + "] is not valid name:level format"); break; } newLevels.put(ROOT, LogLevel.valueOf(level)); break; case 2: newLevels.put(levelNamePair[0].trim(), LogLevel.valueOf(levelNamePair[1].trim().toUpperCase())); break; default: System.err.println(levelStr + " is not valid [name:level] format"); } } Loggers.resetLevel(newLevels); } catch (Exception e) { e.printStackTrace(); } }; static { AppInfo.addObserver(observer); } public static synchronized Loggers create(String name) { System.out.println("create loggers " + name); return new Loggers(name); } private static LogLevel getLevel(final String fullName) { ConcurrentMap cache = _levelMap; if (cache.isEmpty()) { return DEFAULT_LEVEL; } LogLevel level = cache.get(fullName); if (level != null) { return level; } int index = 0; String logName = fullName; do { index = logName.lastIndexOf('.'); if (index <= 0) { break; } logName = logName.substring(0, index); level = cache.get(logName); if (level != null) { cache.put(fullName, level); return level; } } while (logName.length() > 0); cache.put(fullName, DEFAULT_LEVEL); return DEFAULT_LEVEL; } public static LogLevel getLevel(SumkLogger log) { String name = log.getName(); if (name == null || name.isEmpty()) { return DEFAULT_LEVEL; } return getLevel(name); } public synchronized static void resetLevel(Map newLevelMap) { ConcurrentMap newLevels = new ConcurrentHashMap<>(newLevelMap); LogLevel defaultLevel = newLevels.remove(ROOT); DEFAULT_LEVEL = defaultLevel == null ? LogLevel.INFO : defaultLevel; _levelMap = newLevels; } public static void setDefaultLevel(LogLevel level) { if (level != null) { DEFAULT_LEVEL = level; } } public static Map currentLevels() { return Collections.unmodifiableMap(_levelMap); } public SumkLogger get(String name) { return map.get(name); } public SumkLogger putIfAbsent(String name, SumkLogger log) { return map.putIfAbsent(name, log); } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/Logs.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import org.slf4j.Logger; /** * 这个类仅限于sumk框架内部使用 */ public final class Logs { public static Logger http() { return Log.get("sumk.http"); } public static Logger rpc() { return Log.get("sumk.rpc"); } public static Logger system() { return Log.get("sumk.system"); } public static Logger db() { return Log.get("sumk.db"); } public static Logger redis() { return Log.get("sumk.redis"); } public static Logger ioc() { return Log.get("sumk.ioc"); } public static Logger asm() { return Log.get("sumk.ioc.asm"); } public static Logger aop() { return Log.get("sumk.ioc.aop"); } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/RawLog.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import java.util.Objects; public final class RawLog { public static final SimpleLogger CONSOLE_LOG = new SimpleLogger() { @Override public void debug(String module, String msg) { } @Override public void info(String module, String msg) { System.out.println(msg); } @Override public void warn(String module, String msg) { System.out.println(msg); } @Override public void error(String module, String msg, Throwable e) { if (msg != null) { System.out.println(msg); } if (e != null) { e.printStackTrace(); } } @Override public void error(String module, String msg) { System.out.println(msg); } }; public static final SimpleLogger SLF4J_LOG = new SimpleLogger() { @Override public void debug(String module, String msg) { Log.get(module).debug(msg); } @Override public void info(String module, String msg) { Log.get(module).info(msg); } @Override public void warn(String module, String msg) { Log.get(module).warn(msg); } @Override public void error(String module, String msg, Throwable e) { Log.get(module).error(msg, e); } @Override public void error(String module, String msg) { Log.get(module).error(msg); } }; private static SimpleLogger inst = CONSOLE_LOG; public static void setLogger(SimpleLogger inst) { RawLog.inst = Objects.requireNonNull(inst); } public static void debug(String module, String msg) { inst.debug(module, msg); } public static void info(String module, String msg) { inst.info(module, msg); } public static void warn(String module, String msg) { inst.warn(module, msg); } public static void error(String module, String msg) { inst.error(module, msg); } public static void error(String module, Throwable e) { inst.error(module, e.getMessage(), e); } public static void error(String module, String msg, Throwable e) { if (e == null) { inst.error(module, msg); return; } if (msg == null || msg.isEmpty()) { msg = e.toString(); } inst.error(module, msg, e); } } ================================================ FILE: sumk-base/src/main/java/org/yx/log/SimpleLogger.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; public interface SimpleLogger { void debug(String module, String msg); void info(String module, String msg); void warn(String module, String msg); void error(String module, String msg, Throwable e); void error(String module, String msg); } ================================================ FILE: sumk-base/src/main/java/org/yx/log/SumkLogger.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.log; import static org.yx.log.LogLevel.DEBUG; import static org.yx.log.LogLevel.ERROR; import static org.yx.log.LogLevel.INFO; import static org.yx.log.LogLevel.TRACE; import static org.yx.log.LogLevel.WARN; import org.slf4j.Logger; import org.slf4j.Marker; public abstract class SumkLogger implements Logger { protected final String name; /** * 不为null,也不以.开头 */ public String getName() { return name; } protected SumkLogger(String module) { this.name = parseName(module); } private String parseName(String module) { if (module == null) { return ""; } while (module.startsWith(".")) { module = module.substring(1); } return module; } protected boolean isLogable(LogLevel methodLevel) { return methodLevel.ordinal() >= Loggers.getLevel(this).ordinal(); } protected abstract void output(Marker marker, LogLevel methodLevel, String format, Object... arguments); protected abstract void output(Marker marker, LogLevel methodLevel, String msg, Throwable e); private void log(LogLevel methodLevel, String msg) { if (this.isLogable(methodLevel)) { this.output(null, methodLevel, msg); } } private void log(LogLevel methodLevel, String format, Object... arguments) { if (this.isLogable(methodLevel)) { this.output(null, methodLevel, format, arguments); } } private void log(LogLevel methodLevel, String msg, Throwable t) { if (this.isLogable(methodLevel)) { this.output(null, methodLevel, msg, t); } } private void log(Marker marker, LogLevel methodLevel, String msg) { if (this.isLogable(methodLevel)) { this.output(marker, methodLevel, msg); } } private void log(Marker marker, LogLevel methodLevel, String format, Object... arguments) { if (this.isLogable(methodLevel)) { this.output(marker, methodLevel, format, arguments); } } private void log(Marker marker, LogLevel methodLevel, String msg, Throwable t) { if (this.isLogable(methodLevel)) { this.output(marker, methodLevel, msg, t); } } @Override public boolean isTraceEnabled() { return isLogable(TRACE); } @Override public void trace(String msg) { this.log(TRACE, msg); } @Override public void trace(String format, Object arg) { this.log(TRACE, format, arg); } @Override public void trace(String format, Object arg1, Object arg2) { this.log(TRACE, format, arg1, arg2); } @Override public void trace(String format, Object... arguments) { this.log(TRACE, format, arguments); } @Override public void trace(String msg, Throwable t) { this.log(TRACE, msg, t); } @Override public boolean isDebugEnabled() { return isLogable(DEBUG); } @Override public void debug(String msg) { this.log(DEBUG, msg); } @Override public void debug(String format, Object arg) { this.log(DEBUG, format, arg); } @Override public void debug(String format, Object arg1, Object arg2) { this.log(DEBUG, format, arg1, arg2); } @Override public void debug(String format, Object... arguments) { this.log(DEBUG, format, arguments); } @Override public void debug(String msg, Throwable t) { this.log(DEBUG, msg, t); } @Override public boolean isInfoEnabled() { return isLogable(INFO); } @Override public void info(String msg) { this.log(INFO, msg); } @Override public void info(String format, Object arg) { this.log(INFO, format, arg); } @Override public void info(String format, Object arg1, Object arg2) { this.log(INFO, format, arg1, arg2); } @Override public void info(String format, Object... arguments) { this.log(INFO, format, arguments); } @Override public void info(String msg, Throwable t) { this.log(INFO, msg, t); } @Override public boolean isWarnEnabled() { return isLogable(WARN); } @Override public void warn(String msg) { this.log(WARN, msg); } @Override public void warn(String format, Object arg) { this.log(WARN, format, arg); } @Override public void warn(String format, Object arg1, Object arg2) { this.log(WARN, format, arg1, arg2); } @Override public void warn(String format, Object... arguments) { this.log(WARN, format, arguments); } @Override public void warn(String msg, Throwable t) { this.log(WARN, msg, t); } @Override public boolean isErrorEnabled() { return isLogable(ERROR); } @Override public void error(String msg) { this.log(ERROR, msg); } @Override public void error(String format, Object arg) { this.log(ERROR, format, arg); } @Override public void error(String format, Object arg1, Object arg2) { this.log(ERROR, format, arg1, arg2); } @Override public void error(String format, Object... arguments) { this.log(ERROR, format, arguments); } @Override public void error(String msg, Throwable t) { this.log(ERROR, msg, t); } public boolean isON() { return Loggers.getLevel(this) == LogLevel.ON; } @Override public boolean isTraceEnabled(Marker marker) { return this.isTraceEnabled(); } @Override public void trace(Marker marker, String msg) { this.log(marker, TRACE, msg); } @Override public void trace(Marker marker, String format, Object arg) { this.log(marker, TRACE, format, arg); } @Override public void trace(Marker marker, String format, Object arg1, Object arg2) { this.log(marker, TRACE, format, arg1, arg2); } @Override public void trace(Marker marker, String format, Object... argArray) { this.log(marker, TRACE, format, argArray); } @Override public void trace(Marker marker, String msg, Throwable t) { this.log(marker, TRACE, msg, t); } @Override public boolean isDebugEnabled(Marker marker) { return this.isDebugEnabled(); } @Override public void debug(Marker marker, String msg) { this.log(marker, DEBUG, msg); } @Override public void debug(Marker marker, String format, Object arg) { this.log(marker, DEBUG, format, arg); } @Override public void debug(Marker marker, String format, Object arg1, Object arg2) { this.log(marker, DEBUG, format, arg1, arg2); } @Override public void debug(Marker marker, String format, Object... argArray) { this.log(marker, DEBUG, format, argArray); } @Override public void debug(Marker marker, String msg, Throwable t) { this.log(marker, DEBUG, msg, t); } @Override public boolean isInfoEnabled(Marker marker) { return this.isInfoEnabled(); } @Override public void info(Marker marker, String msg) { this.log(marker, INFO, msg); } @Override public void info(Marker marker, String format, Object arg) { this.log(marker, INFO, format, arg); } @Override public void info(Marker marker, String format, Object arg1, Object arg2) { this.log(marker, INFO, format, arg1, arg2); } @Override public void info(Marker marker, String format, Object... argArray) { this.log(marker, INFO, format, argArray); } @Override public void info(Marker marker, String msg, Throwable t) { this.log(marker, INFO, msg, t); } @Override public boolean isWarnEnabled(Marker marker) { return this.isWarnEnabled(); } @Override public void warn(Marker marker, String msg) { this.log(marker, WARN, msg); } @Override public void warn(Marker marker, String format, Object arg) { this.log(marker, WARN, format, arg); } @Override public void warn(Marker marker, String format, Object arg1, Object arg2) { this.log(marker, WARN, format, arg1, arg2); } @Override public void warn(Marker marker, String format, Object... argArray) { this.log(marker, WARN, format, argArray); } @Override public void warn(Marker marker, String msg, Throwable t) { this.log(marker, WARN, msg, t); } @Override public boolean isErrorEnabled(Marker marker) { return this.isErrorEnabled(); } @Override public void error(Marker marker, String msg) { this.log(marker, ERROR, msg); } @Override public void error(Marker marker, String format, Object arg) { this.log(marker, ERROR, format, arg); } @Override public void error(Marker marker, String format, Object arg1, Object arg2) { this.log(marker, ERROR, format, arg1, arg2); } @Override public void error(Marker marker, String format, Object... argArray) { this.log(marker, ERROR, format, argArray); } @Override public void error(Marker marker, String msg, Throwable t) { this.log(marker, ERROR, msg, t); } @Override public String toString() { return this.getClass().getSimpleName() + "[" + name + "]"; } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/BitUtil.java ================================================ package org.yx.util; import org.yx.exception.SumkException; public class BitUtil { public static int setBit(int flag, int rightCount, boolean value) { if (rightCount < 1 || rightCount > 32) { throw new SumkException(23436546, "bit要在1-32之间,实际却是" + rightCount); } int v = 1 << (rightCount - 1); if (value) { return flag | v; } return flag & (~v); } public static boolean getBit(int flag, int rightCount) { if (rightCount < 1 || rightCount > 32) { throw new SumkException(23436546, "bit要在1-32之间,实际却是" + rightCount); } int v = 1 << (rightCount - 1); return (flag & v) != 0; } public static int setBitsToTrue(int flag, int... rightCounts) { for (int bit : rightCounts) { flag = setBit(flag, bit, true); } return flag; } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/CollectionUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import static org.yx.conf.Const.LN; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.yx.annotation.doc.NotNull; import org.yx.base.sumk.UnmodifiableArrayList; import org.yx.base.sumk.map.UnmodifiableListMap; import org.yx.exception.SumkException; import org.yx.log.RawLog; /** * 本类的许多方法都会对key、value做trim()处理 */ public final class CollectionUtil { private static final String IGNORE_PREFIX = "#"; /** * 与后面的有效行合并成一行。如果后面是空行或者#开头的行,就继续寻找下一行。 如果后面是文件结尾,就忽略这个结尾 */ private static final String LINE_CONCAT = "\\"; public static Map loadMapFromText(String text, String bigDelimiter, String smallDelimiter) { return fillMapFromText(new HashMap(), text, bigDelimiter, smallDelimiter); } public static Map fillMapFromText(Map map, String text, String bigDelimiter, String smallDelimiter) { for (String entry : text.split(bigDelimiter)) { entry = entry.trim(); if (StringUtil.isEmpty(entry)) { continue; } String[] vs = entry.split(smallDelimiter, 2); switch (vs.length) { case 1: map.put(vs[0].trim(), null); break; case 2: map.put(vs[0].trim(), vs[1].trim()); break; default: continue; } } return map; } public static String saveMapToText(Map map, String bigDelimiter, String smallDelimiter) { StringBuilder sb = new StringBuilder(); for (Entry entry : map.entrySet()) { String k = entry.getKey(); Object v = entry.getValue(); sb.append(k); if (v != null) { sb.append(smallDelimiter).append(v); } sb.append(bigDelimiter); } return sb.toString(); } public static Map fillPropertiesFromText(final Map map, String text) { if (text == null || text.isEmpty()) { return map; } try (BufferedReader reader = new BufferedReader(new StringReader(text))) { String tmp = null; while ((tmp = readLine(reader)) != null) { String[] kv = tmp.split("=", 2); if (kv.length != 2) { continue; } String k = kv[0].trim(); String v = kv[1].trim(); if (k.isEmpty() || v.isEmpty()) { continue; } map.put(k, v); } } catch (Exception e) { RawLog.error("文本转换成map失败:{}", text); throw new SumkException(-234352398, text, e); } return map; } /** * * @param reader * @return 返回null表示读取结束 * @throws IOException */ private static String readLine(BufferedReader reader) throws IOException { StringBuilder sb = new StringBuilder(); String tmp = null; boolean readed = false; while ((tmp = reader.readLine()) != null) { readed = true; if (tmp.startsWith(IGNORE_PREFIX) || tmp.trim().isEmpty()) { continue; } if (tmp.endsWith(LINE_CONCAT)) { if (tmp.length() == 1) { // 1个字节如果做substring会出错 continue; } sb.append(tmp.substring(0, tmp.length() - 1)); continue; } sb.append(tmp); break; } if (!readed) { return null; } String line = sb.toString(); return line.trim(); } public static List loadList(InputStream in) throws IOException { if (in == null) { return Collections.emptyList(); } byte[] bs = IOUtil.readAllBytes(in, true); if (bs == null || bs.length == 0) { return Collections.emptyList(); } String text = new String(bs, StandardCharsets.UTF_8); text = StringUtil.formatNewLineFlag(text); return StringUtil.splitAndTrim(text, LN); } public static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } public static boolean isEmpty(Collection colletion) { return colletion == null || colletion.isEmpty(); } public static boolean isNotEmpty(Collection colletion) { return colletion != null && colletion.size() > 0; } @SuppressWarnings("unchecked") public static Map flatMapToTree(@NotNull Map map) { Map ret = new HashMap<>(); for (Entry entry : map.entrySet()) { String k = entry.getKey(); String v = entry.getValue(); if (!k.contains(".")) { ret.put(k, v); continue; } String[] ks = k.split("\\."); int lastIndex = ks.length - 1; Map temp = ret; for (int i = 0; i < lastIndex; i++) { String k0 = ks[i]; Object obj = temp.get(k0); if (obj == null) { Map temp2 = new HashMap<>(); temp.put(k0, temp2); temp = temp2; continue; } temp = (Map) obj; continue; } temp.put(ks[lastIndex], v); } return ret; } public static Map subMap(@NotNull Map source, @NotNull String prefix) { int len = prefix.length(); Map map = new HashMap<>(); for (Entry entry : source.entrySet()) { String key = entry.getKey(); T value = entry.getValue(); if (key.startsWith(prefix)) { map.put(key.substring(len), value); } } return map; } /** * 返回一个不可变的list,这个list是原来的副本,它不会保存原来col的引用 * * @param 类型 * @param col 原始集合,可以为null * @return 返回值不可修改,且不为null */ public static List unmodifyList(Collection col) { if (col == null || col.isEmpty()) { return Collections.emptyList(); } if (col instanceof UnmodifiableArrayList) { return (UnmodifiableArrayList) col; } if (col instanceof List) { String clzName = col.getClass().getName(); if ("java.util.Collections$SingletonList".equals(clzName) || "java.util.Collections$UnmodifiableRandomAccessList".equals(clzName) || "java.util.Collections$UnmodifiableList".equals(clzName)) { return (List) col; } } if (col.size() == 1) { return Collections.singletonList(col.iterator().next()); } return new UnmodifiableArrayList<>(col); } /** * 支持参数为null * * @param 类型 * @param arr 原始数组,对原始数组的修改有可能会修改本集合。它可以为null * @return 返回值不可修改,且不为null */ public static List unmodifyList(T[] arr) { if (arr == null || arr.length == 0) { return Collections.emptyList(); } if (arr.length == 1) { return Collections.singletonList(arr[0]); } return new UnmodifiableArrayList<>(arr); } /** * 生成不可变map。如果是特殊map,它的特性可能丢失,比如大小写不敏感特性 * * @param key的类型 * @param value的类型 * @param m 原始map * @return 返回值不可修改,且不为null */ public static Map unmodifyMap(Map m) { if (m == null || m.isEmpty()) { return Collections.emptyMap(); } if (UnmodifiableListMap.class.equals(m.getClass())) { return m; } String clzName = m.getClass().getName(); if ("java.util.Collections$SingletonMap".equals(clzName) || "java.util.Collections$UnmodifiableMap".equals(clzName)) { return m; } final int size = m.size(); if (size == 1) { Entry kv = m.entrySet().iterator().next(); return Collections.singletonMap(kv.getKey(), kv.getValue()); } if (size < 16) { return new UnmodifiableListMap<>(m); } return Collections.unmodifiableMap(m); } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/ExceptionUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.io.PrintWriter; import org.yx.base.sumk.UnsafeStringWriter; public final class ExceptionUtil { public static RuntimeException toRuntimeException(Throwable e) { if (e instanceof RuntimeException) { return (RuntimeException) e; } return new RuntimeException(e); } public static void printStackTrace(StringBuilder sb, Throwable e) { UnsafeStringWriter sw = new UnsafeStringWriter(sb); PrintWriter w = new PrintWriter(sw); e.printStackTrace(w); } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/FileUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.io.File; import java.net.URISyntaxException; import java.util.Collection; import org.yx.conf.AppInfo; public final class FileUtil { /** * 列出该目录下的所有子文件(不包含目录) * * @param filelist 目标对象 * @param parent 目录 */ public static void listAllSubFiles(Collection filelist, File parent) { File[] files = parent.listFiles(); if (files != null) { for (File f : files) { if (f.isDirectory()) { listAllSubFiles(filelist, f); } else { filelist.add(f); } } } } public static File file(String path) throws URISyntaxException { if (path.startsWith(AppInfo.CLASSPATH_URL_PREFIX)) { return new File(Loader.loader().getResource(path.substring(AppInfo.CLASSPATH_URL_PREFIX.length())).toURI()); } return new File(path); } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/IOUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import org.yx.base.sumk.UnsafeByteArrayOutputStream; import org.yx.log.RawLog; public final class IOUtil { public static byte[] readAllBytes(InputStream in, boolean closeInput) throws IOException { UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(1024); transferTo(in, out, closeInput); out.close(); return out.toByteArray(); } public static String readAll(Reader in, boolean closeInput) throws IOException { StringBuilder sb = new StringBuilder(); int len; char[] buf = new char[1024]; try { while ((len = in.read(buf)) > 0) { sb.append(buf, 0, len); } } finally { if (closeInput) { try { in.close(); } catch (Exception e) { RawLog.error("sumk.sys", e); } } } return sb.toString(); } public static int transferTo(InputStream in, OutputStream output, boolean closeInput) throws IOException { if (in == null) { return 0; } try { int n = 0; int count = 0; byte[] temp = new byte[1024]; while (-1 != (n = in.read(temp))) { output.write(temp, 0, n); count += n; } return count; } finally { if (closeInput) { try { in.close(); } catch (Exception e) { RawLog.error("sumk.sys", e); } } } } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/Loader.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.URL; import java.util.Enumeration; public final class Loader { public static final String JAVA_PRE = "java."; private static ClassLoader classLoader; public static void setClassLoader(ClassLoader classLoader) { Loader.classLoader = classLoader; } public static ClassLoader loader() { ClassLoader l = classLoader; return l != null ? l : Loader.class.getClassLoader(); } public static T newInstance(Class clz) throws Exception { Constructor c = clz.getDeclaredConstructor(); if (!c.isAccessible()) { c.setAccessible(true); } return c.newInstance(); } public static Object newInstance(String clz) throws Exception { Class clazz = loadClass(clz); return newInstance(clazz); } public static Object newInstance(String clzName, Object[] params, Class[] paramsType) throws Exception { Class clz = Class.forName(clzName, true, loader()); Constructor c = paramsType == null ? clz.getDeclaredConstructor() : clz.getDeclaredConstructor(paramsType); if (!c.isAccessible()) { c.setAccessible(true); } return c.newInstance(params); } public static Class loadClass(String clz) throws ClassNotFoundException { return loader().loadClass(clz); } public static InputStream getResourceAsStream(String name) { return loader().getResourceAsStream(name); } public static Enumeration getResources(String name) throws IOException { return loader().getResources(name.trim()); } public static URL getResource(String name) { return loader().getResource(name); } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/StringUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.util.ArrayList; import java.util.List; import org.yx.annotation.doc.NotNull; import org.yx.conf.Const; import org.yx.exception.SumkException; public final class StringUtil { public static String formatNewLineFlag(@NotNull String text) { return text.replace("\r\n", Const.LN).replace("\r", Const.LN); } public static String uncapitalize(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } return new StringBuilder(strLen).append(Character.toLowerCase(str.charAt(0))).append(str.substring(1)) .toString(); } /** * 这个会自动对每个子项做trim()操作,并且过滤掉空值 * * @param source 原始字符串,不能为null * @param splitRegex 分隔符 * @param otherSplits 其它的分隔符,这些分隔符不支持正则表达式。结果集会根据所有的分隔符进行分割 * @return 结果不包含null和空字符串。返回值不为null */ public static List splitAndTrim(@NotNull String source, @NotNull String splitRegex, String... otherSplits) { if (otherSplits != null && otherSplits.length > 0) { for (String r : otherSplits) { source = source.replace(r, splitRegex); } } String[] vs = source.split(splitRegex); List list = new ArrayList<>(vs.length); for (String v : vs) { v = v.trim(); if (v.isEmpty()) { continue; } list.add(v); } return list; } public static boolean isEmpty(CharSequence str) { return str == null || str.length() == 0; } public static String toLatin(String v) { return v.replace(',', ',').replace(';', ';').replace(' ', ' ').replace(':', ':').replace('。', '.').replace('?', '?'); } public static String capitalize(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } return new StringBuilder(strLen).append(Character.toUpperCase(str.charAt(0))).append(str.substring(1)) .toString(); } public static boolean isNotEmpty(CharSequence str) { return str != null && str.length() > 0; } public static String camelToUnderline(String param) { if (param == null) { return param; } int len = param.length(); StringBuilder sb = new StringBuilder(len + 10); for (int i = 0; i < len; i++) { char c = param.charAt(i); if (i == 0) { sb.append(Character.toLowerCase(c)); continue; } if (Character.isUpperCase(c)) { sb.append('_'); sb.append(Character.toLowerCase(c)); } else { sb.append(c); } } return sb.toString(); } public static boolean isNumber(char c) { return c >= '0' && c <= '9'; } public static String requireNotEmpty(String text) { if (text == null) { throw new SumkException(652342134, "字符串是null"); } text = text.trim(); if (text.isEmpty()) { throw new SumkException(652342134, "字符串是空的"); } return text; } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/SumkDate.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import java.io.Serializable; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Objects; import org.yx.base.date.DateFormater; import org.yx.base.date.DateTimeFormater; import org.yx.base.date.FullDateTimeFormater; import org.yx.base.date.SumkDateFormater; import org.yx.base.date.SumkDateQuery; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; public final class SumkDate implements Comparable, Serializable { private static final long serialVersionUID = 100L; public static final int SUMKDATE_ERROR_CODE = 912753954; private static final String LOG_NAME = "sumk.date"; public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; public static final String yyyy_MM_dd_HH_mm_ss_SSS = "yyyy-MM-dd HH:mm:ss.SSS"; public static final String yyyy_MM_dd = "yyyy-MM-dd"; public static final String HH_mm_ss = "HH:mm:ss"; public static final String HH_mm_ss_SSS = "HH:mm:ss.SSS"; private static final int MIL_TO_NANO = 1000_000; private static volatile CacheDate CACHED; private static int cacheChangeCount; /** * 只是个预估数字 * * @return 缓存修改的次数 */ public static int cacheChangeCount() { return cacheChangeCount; } private static final SumkDateFormater[] formaters = { FullDateTimeFormater.inst, DateTimeFormater.inst, DateFormater.inst }; /** * @return 当前时间 */ public static SumkDate now() { return create(System.currentTimeMillis(), true); } private static SumkDate create(final long currentMS, boolean toCache) { final long currentSeconds = currentMS / 1000; CacheDate cache = CACHED; if (cache != null) { if (currentSeconds == cache.seconds) { return cache.date.withMilSecond((int) (currentMS % 1000)); } long newSec = currentSeconds - cache.seconds + cache.date.second; if (newSec >= 0 && newSec < 60) { SumkDate c = cache.date; return new SumkDate(c.year, c.month, c.day, c.hour, c.minute, (byte) newSec, (short) (currentMS % 1000)); } } Calendar.Builder builder = new Calendar.Builder(); builder.setInstant(currentMS); SumkDate sd = of(builder.build()); if (toCache) { CACHED = new CacheDate(currentSeconds, sd); cacheChangeCount++; } return sd; } public static SumkDate of(Calendar cal) { int y = cal.get(Calendar.ERA) == GregorianCalendar.AD ? cal.get(Calendar.YEAR) : 1 - cal.get(Calendar.YEAR); return of(y, cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DATE), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND)); } public static SumkDate of(Date d) { return of(d.getTime()); } public static SumkDate of(long timeInMillis) { return create(timeInMillis, false); } /** * * @param year 年份,从公元元年开始计算 * @param month 1-12 * @param day 1-31 * @param hour 0-23 * @param minute 0-59 * @param second 0-59 * @param milSecond 0-999 * @return SumkDate对象 */ public static SumkDate of(int year, int month, int day, int hour, int minute, int second, int milSecond) { return new SumkDate(year, (byte) month, (byte) day, (byte) hour, (byte) minute, (byte) second, (short) milSecond); } SumkDate(int year, byte month, byte day, byte hour, byte minute, byte second, short milSecond) { if (month < 1 || month > 12) { throw new SumkException(SUMKDATE_ERROR_CODE, month + " is not valid month"); } if (day < 1 || day > 31) { throw new SumkException(SUMKDATE_ERROR_CODE, day + " is not valid day"); } if (hour < 0 || hour > 23) { throw new SumkException(SUMKDATE_ERROR_CODE, hour + " is not valid hour"); } if (minute < 0 || minute > 59) { throw new SumkException(SUMKDATE_ERROR_CODE, minute + " is not valid minute"); } if (second < 0 || second > 59) { throw new SumkException(SUMKDATE_ERROR_CODE, second + " is not valid second"); } if (milSecond < 0 || milSecond > 999) { throw new SumkException(SUMKDATE_ERROR_CODE, milSecond + " is not valid milSecond"); } this.year = year; this.month = month; this.day = day; this.hour = hour; this.minute = minute; this.second = second; this.milSecond = milSecond; } public static SumkDate of(LocalDateTime d) { return of(d.toLocalDate(), d.toLocalTime()); } /** * @param date 可以为null,如果为null,年份就是1970-01-01 * @param time 可以为null * @return SumkDate对象 */ public static SumkDate of(LocalDate date, LocalTime time) { int year, month, day; if (date != null) { year = date.getYear(); month = date.getMonthValue(); day = date.getDayOfMonth(); } else { year = 1970; month = 1; day = 1; } if (time != null) { return of(year, month, day, time.getHour(), time.getMinute(), time.getSecond(), time.get(ChronoField.MILLI_OF_SECOND)); } return new SumkDate(year, (byte) month, (byte) day, (byte) 0, (byte) 0, (byte) 0, (byte) 0); } public static SumkDate of(final String dateString) { int lastDot = dateString.lastIndexOf('.'); if (lastDot > 15 && dateString.length() - lastDot <= 4) { return FullDateTimeFormater.inst.parse(dateString); } return DateTimeFormater.inst.parse(dateString); } public static SumkDate of(final String dateString, final String format) { for (SumkDateFormater f : formaters) { if (f.accept(format)) { return f.parse(dateString); } } try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); SumkDate sd = formatter.parse(dateString, SumkDateQuery.inst); if (sd != null) { return sd; } } catch (Exception e) { Log.get(LOG_NAME).warn(e.getMessage(), e); if (!AppInfo.getBoolean("sumk.date.retry_simple", true)) { throw new SumkException(SUMKDATE_ERROR_CODE, dateString + "使用java time方式解析失败", e); } } try { return of(new SimpleDateFormat(format).parse(dateString)); } catch (ParseException e1) { Log.get(LOG_NAME).error(e1.getMessage(), e1); throw new SumkException(SUMKDATE_ERROR_CODE, dateString + "使用SimpleDateFormat方式解析失败", e1); } } /** * 将日期转化为字符串 * * @param d 不能为null * @param format 格式 * @return SumkDate对象,如果日期为null,就返回null */ public static String format(Date d, String format) { if (d == null) { return null; } if (yyyy_MM_dd_HH_mm_ss_SSS.equals(format)) { return of(d).to_yyyy_MM_dd_HH_mm_ss_SSS(); } if (yyyy_MM_dd_HH_mm_ss.equals(format)) { return of(d).to_yyyy_MM_dd_HH_mm_ss(); } if (yyyy_MM_dd.equals(format)) { return of(d).to_yyyy_MM_dd(); } if (HH_mm_ss.equals(format)) { return of(d).to_HH_mm_ss(); } if (HH_mm_ss_SSS.equals(format)) { return of(d).to_HH_mm_ss_SSS(); } return new SimpleDateFormat(format).format(d); } public static String format(LocalDateTime d, String format) { if (d == null) { return null; } if (yyyy_MM_dd_HH_mm_ss_SSS.equals(format)) { return of(d).to_yyyy_MM_dd_HH_mm_ss_SSS(); } if (yyyy_MM_dd_HH_mm_ss.equals(format)) { return of(d).to_yyyy_MM_dd_HH_mm_ss(); } if (yyyy_MM_dd.equals(format)) { return of(d).to_yyyy_MM_dd(); } if (HH_mm_ss.equals(format)) { return of(d).to_HH_mm_ss(); } if (HH_mm_ss_SSS.equals(format)) { return of(d).to_HH_mm_ss_SSS(); } return d.format(DateTimeFormatter.ofPattern(format)); } public static Date toDate(String dateString, String format) { return of(dateString, format).toDate(); } public static LocalDateTime toLocalDateTime(String dateString, String format) { return of(dateString, format).toLocalDateTime(); } public static Date toDate(LocalDateTime localDateTime) { return of(localDateTime).toDate(); } public static LocalDateTime toLocalDateTime(Date date) { return of(date).toLocalDateTime(); } public static long takeUpTimeInMils(SumkDate from, SumkDate to) { return to.getTimeInMils() - from.getTimeInMils(); } final int year; final byte month; final byte day; final byte hour; final byte minute; final byte second; final short milSecond; /** * @return 日期中的天,1-31 * */ public int getDay() { return day; } /** * @return 24小时制,0-23 */ public int getHour() { return hour; } /** * @return 毫秒,0-999 */ public int getMilSecond() { return milSecond; } /** * @return 分钟,0-59 */ public int getMinute() { return minute; } /** * @return 月份,1-12 */ public int getMonth() { return month; } /** * @return 秒,0-59 */ public int getSecond() { return second; } /** * @return 按公元纪年。公元前的年份可能会有问题,因为该年份不会小于0 * */ public int getYear() { return year; } /** * 返回的格式如13:12:59 * * @return HH:mm:ss 格式 */ public String to_HH_mm_ss() { return new UnsafeFormater(this).to_HH_mm_ss().toString(); } /** * 返回的格式如13:12:59.123 * * @return HH:mm:ss.SSS 格式 */ public String to_HH_mm_ss_SSS() { return new UnsafeFormater(this).to_HH_mm_ss_SSS().toString(); } /** * 返回的格式如2018-10 * * @return yyyy-MM格式 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ public String to_yyyy_MM() { return new UnsafeFormater(this).to_yyyy_MM().toString(); } /** * 返回的格式如2018-10-20 * * @return yyyy-MM-dd格式 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ public String to_yyyy_MM_dd() { return new UnsafeFormater(this).to_yyyy_MM_dd().toString(); } /** * 返回的格式如2018-10-20 13:12:59 * * @return yyyy-MM-dd HH:mm:ss 格式
* 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ public String to_yyyy_MM_dd_HH_mm_ss() { return new UnsafeFormater(this).to_yyyy_MM_dd_HH_mm_ss().toString(); } /** * 返回的格式如2018-10-20 13:12:59.123 * * @return yyyy-MM-dd HH:mm:ss.SSS 格式 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ public String to_yyyy_MM_dd_HH_mm_ss_SSS() { return new UnsafeFormater(this).to_yyyy_MM_dd_HH_mm_ss_SSS().toString(); } public Calendar toCalendar() { Calendar cal = Calendar.getInstance(); cal.set(year, month - 1, day, hour, minute, second); cal.set(Calendar.MILLISECOND, milSecond); return cal; } public Date toDate() { return toCalendar().getTime(); } public long getTimeInMils() { return toCalendar().getTimeInMillis(); } public LocalDate toLocalDate() { return LocalDate.of(year, month, day); } public LocalDateTime toLocalDateTime() { return LocalDateTime.of(toLocalDate(), toLocalTime()); } public LocalTime toLocalTime() { return LocalTime.of(hour, minute, second, milSecond * MIL_TO_NANO); } @SuppressWarnings("deprecation") public Timestamp toTimestamp() { return new Timestamp(getYear() - 1900, getMonth() - 1, getDay(), getHour(), getMinute(), getSecond(), this.milSecond * 1_000_000); } public boolean isAfter(SumkDate d) { return this.compareTo(d) > 0; } public boolean isBefore(SumkDate d) { return this.compareTo(d) < 0; } /** * @return yyyy-MM-dd HH:mm:ss.SSS 格式 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ @Override public String toString() { return to_yyyy_MM_dd_HH_mm_ss_SSS(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + day; result = prime * result + hour; result = prime * result + milSecond; result = prime * result + minute; result = prime * result + month; result = prime * result + second; result = prime * result + year; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SumkDate other = (SumkDate) obj; if (day != other.day) return false; if (hour != other.hour) return false; if (milSecond != other.milSecond) return false; if (minute != other.minute) return false; if (month != other.month) return false; if (second != other.second) return false; if (year != other.year) return false; return true; } @Override public int compareTo(SumkDate d) { if (d == null) { return 1; } if (this.year != d.year) { return year > d.year ? 1 : -1; } int v = this.month - d.month; if (v != 0) { return v; } v = this.day - d.day; if (v != 0) { return v; } v = this.hour - d.hour; if (v != 0) { return v; } v = this.minute - d.minute; if (v != 0) { return v; } v = this.second - d.second; if (v != 0) { return v; } return this.milSecond - d.milSecond; } public SumkDate withYear(int year) { if (year == this.year) { return this; } return new SumkDate(year, month, day, hour, minute, second, milSecond); } /** * 设置月份(不修改原来的对象) * * @param month 1-12 * @return 新的SumkDate对象 */ public SumkDate withMonth(int month) { if (month == this.month) { return this; } if (month < 1 || month > 12) { throw new DateTimeException( new StringBuilder().append("月份参数 ").append(month).append(" 不在有效值范围内").toString()); } return new SumkDate(year, (byte) month, day, hour, minute, second, milSecond); } public SumkDate withDay(int day) { if (day == this.day) { return this; } DAY_OF_MONTH.checkValidValue(day); return new SumkDate(year, month, (byte) day, hour, minute, second, milSecond); } public SumkDate withHour(int h) { if (h == this.hour) { return this; } if (h < 0 || h > 23) { throw new DateTimeException(new StringBuilder().append("小时参数 ").append(h).append(" 不在有效值范围内").toString()); } return new SumkDate(year, month, day, (byte) h, minute, second, milSecond); } public SumkDate withMinute(int m) { if (m == this.minute) { return this; } if (m < 0 || m > 59) { throw new DateTimeException(new StringBuilder().append("分钟参数 ").append(m).append(" 不在有效值范围内").toString()); } return new SumkDate(year, month, day, hour, (byte) m, second, milSecond); } public SumkDate withSecond(int sec) { if (sec == this.second) { return this; } if (sec < 0 || sec > 59) { throw new DateTimeException(new StringBuilder().append("秒参数 ").append(sec).append(" 不在有效值范围内").toString()); } return new SumkDate(year, month, day, hour, minute, (byte) sec, milSecond); } public SumkDate withMilSecond(int milSecond) { if (milSecond == this.milSecond) { return this; } if (milSecond < 0 || milSecond > 999) { throw new DateTimeException( new StringBuilder().append("毫秒参数 ").append(milSecond).append(" 不在有效值范围内").toString()); } return new SumkDate(year, month, day, hour, minute, second, (short) milSecond); } public SumkDate plusYears(int years) { if (years == 0) { return this; } return new SumkDate(year + years, month, day, hour, minute, second, milSecond); } /** * months个月以后的日期 * * @param months 任意数字,正数、负数都可以 * @return months个月以后的日期 */ public SumkDate plusMonths(int months) { if (months == 0) { return this; } return of(this.toLocalDateTime().plusMonths(months)); } public SumkDate plusDays(int days) { if (days == 0) { return this; } return of(this.toLocalDateTime().plusDays(days)); } public SumkDate plusHours(int hours) { if (hours == 0) { return this; } return of(this.toLocalDateTime().plusHours(hours)); } public SumkDate plusMinutes(int minutes) { if (minutes == 0) { return this; } return of(this.toLocalDateTime().plusMinutes(minutes)); } public SumkDate plusSeconds(int seconds) { if (seconds == 0) { return this; } return of(this.toLocalDateTime().plusSeconds(seconds)); } public SumkDate plusMilSeconds(int mils) { if (mils == 0) { return this; } return of(this.toLocalDateTime().plus(mils, ChronoUnit.MILLIS)); } public static String stringOf(Date d) { return d == null ? "null" : of(d).toString(); } public static String stringOf(LocalDateTime d) { return d == null ? "null" : of(d).toString(); } private static final class CacheDate { final long seconds; final SumkDate date; CacheDate(long second, SumkDate date) { this.seconds = second; this.date = Objects.requireNonNull(date); } } private static final class UnsafeFormater { private static final char DATE_SPLIT = '-'; private static final char TIME_SPLIT = ':'; private final SumkDate sumkDate; private final StringBuilder sb = new StringBuilder(26); UnsafeFormater(SumkDate sumkDate) { this.sumkDate = sumkDate; } void appendDoubleChar(byte v) { if (v < 10) { sb.append('0'); } sb.append(v); } /** * 返回的格式如13:12:59 */ UnsafeFormater to_HH_mm_ss() { appendDoubleChar(sumkDate.hour); sb.append(TIME_SPLIT); appendDoubleChar(sumkDate.minute); sb.append(TIME_SPLIT); appendDoubleChar(sumkDate.second); return this; } /** * 返回的格式如13:12:59.123 */ UnsafeFormater to_HH_mm_ss_SSS() { this.to_HH_mm_ss(); sb.append('.'); int mil = sumkDate.milSecond; if (mil < 10) { sb.append("00"); } else if (mil < 100) { sb.append('0'); } sb.append(mil); return this; } /** * 返回的格式如2018-10
* 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ UnsafeFormater to_yyyy_MM() { int y = sumkDate.year; if (y == Integer.MIN_VALUE) { sb.append(y); } else { if (y < 0) { sb.append('-'); y = -y; } if (y < 1000) { if (y >= 100) { sb.append('0'); } else if (y >= 10) { sb.append("00"); } else { sb.append("000"); } } sb.append(y); } sb.append(DATE_SPLIT); appendDoubleChar(sumkDate.month); return this; } /** * 返回的格式如2018-10-20 * * @return yyyy-MM-dd格式 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ UnsafeFormater to_yyyy_MM_dd() { this.to_yyyy_MM(); sb.append(DATE_SPLIT); appendDoubleChar(sumkDate.day); return this; } /** * 返回的格式如2018-10-20 13:12:59 * * @return yyyy-MM-dd HH:mm:ss 格式
* 如果年份小于1000,会在年份前面补上0。与SimpleDateFormat兼容 */ UnsafeFormater to_yyyy_MM_dd_HH_mm_ss() { this.to_yyyy_MM_dd(); sb.append(' '); this.to_HH_mm_ss(); return this; } UnsafeFormater to_yyyy_MM_dd_HH_mm_ss_SSS() { this.to_yyyy_MM_dd(); sb.append(' '); this.to_HH_mm_ss_SSS(); return this; } @Override public String toString() { return sb.toString(); } } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/SumkThreadPool.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.yx.base.context.AppContext; import org.yx.base.thread.SumkExecutorService; import org.yx.base.thread.ThreadPools; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.log.RawLog; public final class SumkThreadPool { private static boolean daemon; public static void setDaemon(boolean daemon) { SumkThreadPool.daemon = daemon; } public static ThreadFactory createThreadFactory(String pre) { return new ThreadFactory() { private final AtomicInteger threadNumber = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, pre + threadNumber.getAndIncrement()); t.setDaemon(daemon); if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; } }; } private static final ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor( Integer.getInteger("sumk.thread.schedule.size", 1), r -> { Thread t = new Thread(r, "sumk-task"); t.setDaemon(true); if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; }); static ScheduledExecutorService scheduledExecutor() { return scheduledExecutor; } public static SumkExecutorService executor() { return ThreadPools.DEFAULT_EXECUTOR; } public static ScheduledFuture scheduleAtFixedRate(Runnable job, long delayMS, long periodMS) { if (AppContext.inst().isDestoryed()) { return null; } if (periodMS <= 0) { throw new SumkException(5674354, "delayMils要大于0 才行"); } else if (periodMS < 100) { RawLog.warn("sumk.thread", job + "加入到作业中(短作业),定时间隔为" + periodMS + "ms"); } else { RawLog.info("sumk.thread", job + "加入到作业中,定时间隔为" + periodMS + "ms"); } SynchronizedRunner single = new SynchronizedRunner(job); Runnable task = () -> { if (single.isBusy()) { return; } try { executor().execute(single); single.working = 1; } catch (Exception e) { RawLog.error("sumk.thread", "添加定时任务失败", e); } }; return scheduledExecutor.scheduleAtFixedRate(task, delayMS, periodMS, TimeUnit.MILLISECONDS); } public static ScheduledFuture schedule(Runnable job, long delayMS) { return scheduledExecutor.schedule(() -> executor().execute(job), delayMS, TimeUnit.MILLISECONDS); } public static void shutdown() { scheduledExecutor.shutdown(); executor().shutdown(); } public static Runnable synchronize(Runnable target) { return new SynchronizedRunner(target); } private static final class SynchronizedRunner implements Runnable { private final ReentrantLock lock; private final Runnable target; int working; public SynchronizedRunner(Runnable target) { this.target = target; this.lock = new ReentrantLock(); } @Override public void run() { if (!lock.tryLock()) { this.working = 0; return; } try { target.run(); } catch (Throwable e) { RawLog.error("sumk.thread", target + "执行失败," + e.getLocalizedMessage(), e); } finally { this.working = 0; lock.unlock(); } } public boolean isBusy() { if (lock.isLocked()) { return true; } if (this.working != 0) { this.working++; if (this.working > 50) { this.working = 0; } return true; } return false; } } public static void scheduleThreadPoolMonitor() { long period = AppInfo.getLong("sumk.threadpool.task.period", 10_000); scheduledExecutor.scheduleAtFixedRate(new ThreadPoolReSeter(), period, period, TimeUnit.MILLISECONDS); } private static class ThreadPoolReSeter implements Runnable { private Logger logger = Log.get("sumk.thread"); @Override public void run() { try { resetCurrentThreshold(); resetThreadPoolSize(); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); } } private void resetCurrentThreshold() { int threshold = AppInfo.getInt("sumk.core.threadpool.threshold", 0); SumkExecutorService executor = SumkThreadPool.executor(); if (threshold > 0) { executor.threshold(threshold); return; } threshold = executor.getPoolSize() + executor.getQueued(); executor.threshold(threshold); logger.trace("set pool threshold to {}", threshold); } private void resetThreadPoolSize() { SumkExecutorService pool = SumkThreadPool.executor(); int size = AppInfo.getInt("sumk.core.threadpool.core", 0); if (size > 0 && pool.getCorePoolSize() != size) { logger.info("change ThreadPool size from {} to {}", pool.getCorePoolSize(), size); pool.setCorePoolSize(size); } size = AppInfo.getInt("sumk.core.threadpool.max", 0); if (size > 0 && pool.getMaximumPoolSize() != size) { logger.info("change ThreadPool max size from {} to {}", pool.getMaximumPoolSize(), size); pool.setMaximumPoolSize(size); } String v = AppInfo.get("sumk.core.threadpool.allowCoreThreadTimeOut", null); if (v != null) { boolean allowCoreTimeout = "1".equals(v) || "true".equalsIgnoreCase(v); if (allowCoreTimeout != pool.allowsCoreThreadTimeOut()) { logger.info("change ThreadPool allowsCoreThreadTimeOut from {} to {}", pool.allowsCoreThreadTimeOut(), allowCoreTimeout); pool.allowCoreThreadTimeOut(allowCoreTimeout); } } } } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/Task.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * 这个定时任务与jvm自带的不同点在于:
* 1、它不会因为异常而停止。
* 2、它使用一个线程负责定时器,触发后由主线程池运行。但它也保证任务不会被并发
*/ public final class Task { public static ScheduledFuture scheduleAtFixedRate(Runnable job, long delayMS, long periodMS) { return SumkThreadPool.scheduleAtFixedRate(job, delayMS, periodMS); } public static ScheduledFuture schedule(Runnable job, long delayMS) { return SumkThreadPool.schedule(job, delayMS); } public static ScheduledFuture scheduleAtFixedRate(Runnable job, long delay, long period, TimeUnit unit) { return scheduleAtFixedRate(job, unit.toMillis(delay), unit.toMillis(period)); } } ================================================ FILE: sumk-base/src/main/java/org/yx/util/UUIDSeed.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.util; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; public final class UUIDSeed { private static final char[] LETTERS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; private static final int LEN = LETTERS.length; private static final int DOUBLE_LEN = LEN * LEN; private static AtomicInteger current = new AtomicInteger(6538); static void fill(char[] source, final int from, int bytes, long number) { if (number < 0) { number = Math.max(0 - number, 0); } int index = from + bytes - 1; while (number > 0) { int k = (int) (number % LEN); source[index] = LETTERS[k]; if (index == from) { return; } number = number / LEN; index--; } while (index >= from) { source[index--] = '0'; } } public static long toLong(String s) { s = s.toLowerCase(); String all = new String(LETTERS); long ret = 0; for (int i = s.length() - 1, k = 0; i >= 0; i--, k++) { char c = s.charAt(i); int v = all.indexOf(String.valueOf(c)); ret += (v * Math.pow(LEN, k)); } return ret; } public static long getSeqTime(String sn) { return toLong(sn.substring(0, 8)); } public static long getRandomSNTime(String sn) { char[] k = sn.toCharArray(); String d = new String(new char[] { k[8], k[12], k[16], k[1], k[5], k[9], k[13], k[17] }); return toLong(d); } public static String seq() { return new String(seqChars()); } public static String seq18() { return new String(seqChars(), 1, 18); } public static String random() { return reOrder(seqChars()); } static char[] seqChars() { ThreadLocalRandom r = ThreadLocalRandom.current(); char[] ret = new char[20]; fill(ret, 0, 8, System.currentTimeMillis()); fill(ret, 8, 4, System.nanoTime()); fill(ret, 12, 2, r.nextInt(DOUBLE_LEN)); int addNum = r.nextInt(LEN) + 1; int next = current.addAndGet(addNum); if (next > 1000000000) { current = new AtomicInteger(next % (DOUBLE_LEN * DOUBLE_LEN) + DOUBLE_LEN); } fill(ret, 14, 4, next); fill(ret, 18, 1, addNum); fill(ret, 19, 1, r.nextInt(LEN)); return ret; } static String reOrder(char[] cs) { char g1 = cs[12]; char g2 = cs[13]; System.arraycopy(cs, 0, cs, 2, 8); cs[0] = g1; cs[1] = g2; char[] temp = new char[cs.length]; int len = cs.length / 2; for (int k = 0; k < 2; k++) { for (int i = 0; i < len; i++) { temp[i * 2] = cs[i]; temp[2 * i + 1] = cs[i + len]; } cs = temp; temp = new char[cs.length]; } return new String(cs); } public static String parse(long number, int bytes) { char[] cs = new char[bytes]; fill(cs, 0, bytes, number); return new String(cs); } } ================================================ FILE: sumk-db/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: sumk-db/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-db com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots youtongluan 3205207767@qq.com https://www.oschina.net/p/sumk com.github.youtongluan sumk-framework ${project.version} com.github.youtongluan async-logger ${project.version} true org.mybatis mybatis true org.apache.commons commons-dbcp2 commons-logging commons-logging mysql mysql-connector-java redis.clients jedis junit junit test ================================================ FILE: sumk-db/src/main/java/org/yx/annotation/db/AutoCreateTime.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.db; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 在@Table注解的表中,让插入记录的时候,一并写入创建时间。它有以下要点: *
    *
  1. 它注解的字段要是时间类型,比如Timestamp、Date、LocalDatetime
  2. *
  3. 只有在单主键,并且该主键是Long类型时才有用
  4. *
  5. 不限制一张表有几个CreateTime字段,0个或多个都是可以的
  6. *
  7. 它只作用在insert的时候,之后它就跟其它字段没啥差异
  8. *
*/ @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AutoCreateTime { } ================================================ FILE: sumk-db/src/main/java/org/yx/annotation/db/Box.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.db; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.yx.conf.Const; import org.yx.db.enums.DBType; import org.yx.db.enums.TransactionType; /** * 注解在当前类的public或protected方法上。不支持在超类或接口上进行注解 */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Box { String value() default Const.DEFAULT_DB_NAME; DBType dbType() default DBType.ANY; TransactionType transaction() default TransactionType.REQUIRED; } ================================================ FILE: sumk-db/src/main/java/org/yx/annotation/db/Column.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.db; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.yx.base.Ordered; import org.yx.db.enums.ColumnType; @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Column { /** * @return 数据库字段的名字,不填的话,就是属性名(小写) */ String value() default ""; ColumnType type() default ColumnType.NORMAL; byte order() default Ordered.DEFAULT_ORDER; } ================================================ FILE: sumk-db/src/main/java/org/yx/annotation/db/SoftDelete.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.db; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.yx.db.enums.ValidRecord; /** * 有这个标志,就说明是软删除
* 支持pojo中存在与软删除同名的字段(大小写不敏感),通常只在whatIsValid 这时insert和udpate的时候,要注意该字段的状态位。 * * @author 游夏 * */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SoftDelete { /** * @return 数据库中字段的名字 */ String value(); /** * @return 只能是String、Integer、Byte、Short、Long、Boolean或者它们的原始类型 */ Class type() default String.class; /** * @return 如果是数字类型,会被转化成数字类型
* 如果是Boolean类型,这个属性没有意义 * */ String validValue() default "1"; /** * @return 如果是数字类型,会被转化成数字类型
* 如果是Boolean类型,这个属性没有意义 * */ String inValidValue() default "0"; ValidRecord whatIsValid() default ValidRecord.EQUAL_VALID; } ================================================ FILE: sumk-db/src/main/java/org/yx/annotation/db/Table.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.db; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.yx.db.enums.CacheType; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Table { /** * @return 表名。为空时,就是表名,支持#或者?作为通配符 */ String value() default ""; /** * @return 在缓存中保留的时间,单位秒。0表示使用全局设置,小于0表示不过期 */ int duration() default 0; /** * 如果使用cluster,同一张表的DB缓存会在一个slot上,这是为了防止mget、mset出问题 * * @return 为空使用表名,一般使用默认就好。支持#或者?作为通配符 */ String preInCache() default ""; /** * @return 访问多少次之后刷新缓存,0表示使用全局默认,小于0表示不刷新 */ int maxHit() default 0; /** * @return 主键缓存都是SINGLE,外键缓存一般用LIST */ CacheType cacheType() default CacheType.SINGLE; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/DB.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db; import java.sql.SQLException; import java.util.Objects; import java.util.function.Consumer; import org.yx.db.conn.ConnectionPool; import org.yx.db.conn.HookContext; import org.yx.db.enums.TxHook; import org.yx.db.exec.DBExecutor; import org.yx.db.exec.DBSource; import org.yx.db.exec.DBTransaction; import org.yx.db.sql.DBFactory; import org.yx.db.sql.Delete; import org.yx.db.sql.Insert; import org.yx.db.sql.Select; import org.yx.db.sql.Update; import org.yx.util.ExceptionUtil; /** * ORM的入口。 本类如果使用Map做参数,map中的key一律是java字段名。
* 大小写敏感性的原则是:数据库字段大小写不敏感,java字段大小写敏感。所以本类中的参数大小写敏感 * * @author 游夏 * */ public final class DB { /** * 进行插入,如果主键是单主键,并且主键是Long类型。 可以不用显示设置主键,系统会自动生成主键
* 要执行execute方法才能生效 * * @return Insert对象 */ public static Insert insert() { return DBFactory.insert(); } /** * 将pojo插入到数据库中
* 要执行execute方法才能生效 * * @param pojo pojo、map或pojo所对应的class对象 * @return Insert对象 */ public static Insert insert(Object pojo) { if (pojo instanceof Class) { return insert().tableClass((Class) pojo); } return insert().insert(pojo); } /** * 默认是局部更新,要调用fullUpdate(),才能进行全部更新。所有的更新都只能根据主键或者redis主键
* 要执行execute方法才能生效
* 如果显式指定where条件,每个条件里都必须包含所有的redis主键字段 * * @return Update对象 */ public static Update update() { return DBFactory.update(); } /** * 默认是局部更新,要调用fullUpdate(),才能进行全部更新。
* 要执行execute方法才能生效
* 如果显式指定where条件,每个条件里都必须包含所有的redis主键字段 * * @param pojo 修改后的pojo值,如果没有显式设置where条件,那么它的条件就是数据库主键或者redis主键 * @return Update对象 */ public static Update update(Object pojo) { if (pojo instanceof Class) { return update().tableClass((Class) pojo); } return update().updateTo(pojo); } public static Delete delete() { return DBFactory.delete(); } /** * 删除(包括软删除)pojo对象定义
* 要执行execute方法才能生效 * * @param pojo pojo、map或pojo所对应的class对象 * @return Delete对象 */ public static Delete delete(Object pojo) { if (pojo instanceof Class) { return delete().tableClass((Class) pojo); } return delete().delete(pojo); } public static Select select() { return DBFactory.select(); } public static Select select(Object pojo) { if (pojo instanceof Class) { return select().tableClass((Class) pojo); } return select().addEqual(pojo); } public static void commit() throws SQLException { ConnectionPool.get().commit(); } public static void rollback(Exception e) throws SQLException { ConnectionPool.get().rollback(e); } public static void addHook(TxHook type, Consumer consumer) { ConnectionPool.get().addHook(type, consumer); } public static T execute(DBSource ds, DBExecutor executor) throws RuntimeException { DBTransaction tran = new DBTransaction(Objects.requireNonNull(ds)); T ret = null; try { tran.begin(); ret = executor.execute(ds); tran.commit(); } catch (Exception e) { tran.rollback(e); throw ExceptionUtil.toRuntimeException(e); } finally { tran.close(); } return ret; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/DBJson.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db; import java.util.Objects; import org.yx.common.json.ByteArrayTypeAdapter; import org.yx.common.json.GsonHelper; import org.yx.common.json.GsonOperator; import org.yx.common.json.JsonOperator; public final class DBJson { private static JsonOperator operator = new GsonOperator( GsonHelper.builder("sumk.db").registerTypeAdapter(byte[].class, ByteArrayTypeAdapter.inst).create()); public static JsonOperator operator() { return operator; } public static void setOperator(JsonOperator operator) { DBJson.operator = Objects.requireNonNull(operator); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/SDB.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db; import java.util.List; import java.util.Map; import org.yx.db.kit.DBKits; import org.yx.db.kit.SDBuilder; import org.yx.db.mapper.NamedExecutor; import org.yx.db.mapper.SqlHolder; import org.yx.db.sql.InsertResult; public class SDB { public static int execute(String name, Map map) { return NamedExecutor.execute(SqlHolder.findSql(name), map); } public static InsertResult insertWithAutoGeneratedKeys(String name, Map map) { return NamedExecutor.insertWithAutoGeneratedKeys(SqlHolder.findSql(name), map); } public static List> list(String name, Map map) { return NamedExecutor.list(SqlHolder.findSql(name), map); } public static List listInArray(String name, Map map) { return NamedExecutor.listInArray(SqlHolder.findSql(name), map); } public static List singleColumnList(String name, Map map) { return NamedExecutor.singleColumnList(SqlHolder.findSql(name), map); } public static long count(String name, Map map) { return NamedExecutor.count(SqlHolder.findSql(name), map); } public static Map queryOne(String name, Map map) { return DBKits.queryOne(list(name, map)); } public static SDBuilder builder() { return new SDBuilder(); } public static SDBuilder builder(String name, Object param) { return new SDBuilder().name(name).param(param); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/CommonConfigFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.yx.annotation.Bean; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.util.CollectionUtil; @Bean public class CommonConfigFactory implements DBConfigFactory { protected Map> parseFromAppInfo(String dbName) { Map map = AppInfo.subMap("s.db." + dbName + "."); if (map.isEmpty()) { return null; } Map configMap = CollectionUtil.flatMapToTree(map); return pureMap(configMap); } @Override public List create(String name) throws Exception { Map> maps = parseFromAppInfo(name); List list = new ArrayList<>(); if (maps == null || maps.isEmpty()) { return list; } for (String index : maps.keySet()) { list.add(DBConfig.create(index, maps.get(index))); } return list; } public static Map> pureMap(Map objectMap) { if (objectMap == null || objectMap.isEmpty()) { return Collections.emptyMap(); } final Map> ret = new HashMap>(); for (Entry entry : objectMap.entrySet()) { String catagory = entry.getKey(); Object value = entry.getValue(); if (!(value instanceof Map)) { Logs.db().info("{} is not valid config, value : {}", catagory, value); continue; } @SuppressWarnings("unchecked") Map config = (Map) value; Map real = new HashMap<>(); for (Entry e2 : config.entrySet()) { String propertyName = e2.getKey(); Object v = e2.getValue(); if (!(v instanceof String)) { Logs.db().info("{}.{} is not valid config,value:{}", catagory, propertyName, v); continue; } real.put(propertyName, (String) v); } if (real.size() > 0) { ret.put(catagory, real); } } return ret; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/ConnectionPool.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import org.slf4j.Logger; import org.yx.base.context.ActionContext; import org.yx.common.util.kit.Asserts; import org.yx.db.enums.DBType; import org.yx.db.enums.TxHook; import org.yx.db.event.DBEvent; import org.yx.db.event.EventLane; import org.yx.db.sql.PojoMeta; import org.yx.exception.SimpleSumkException; import org.yx.exception.SumkException; import org.yx.log.Log; public final class ConnectionPool implements AutoCloseable { private static ThreadLocal> connectionHolder = new ThreadLocal>() { @Override protected List initialValue() { return new ArrayList<>(2); } }; private static final Logger LOGGER = Log.get("sumk.conn"); private final String dbName; private final DBType dbType; private final boolean autoCommit; private SumkConnection readConn; private SumkConnection writeConn; private List hooks; private final EventLane eventLane; public void addHook(TxHook type, Consumer r) { SqlSessionHook act = new SqlSessionHook(type, r); if (hooks == null) { hooks = new ArrayList<>(); } if (hooks.contains(act)) { return; } hooks.add(act); } public static ConnectionPool create(String dbName, DBType dbType, boolean autoCommit) { if (ActionContext.current().isTest() && !autoCommit) { if (connectionHolder.get().size() > 0) { return null; } dbType = DBType.WRITE; } List list = connectionHolder.get(); ConnectionPool context = new ConnectionPool(dbName, dbType, autoCommit); list.add(0, context); return context; } public static ConnectionPool createIfAbsent(String dbName, DBType dbType) { List list = connectionHolder.get(); if (list.size() > 0 && list.get(0).getDbName().equals(dbName)) { return null; } return create(dbName, dbType, false); } private ConnectionPool(String dbName, DBType dbType, boolean autoCommit) { this.dbName = Objects.requireNonNull(dbName); this.dbType = Objects.requireNonNull(dbType); this.autoCommit = autoCommit; this.eventLane = new EventLane(); } public static ConnectionPool get() { List list = connectionHolder.get(); Asserts.requireTrue(list.size() > 0, "must open transaction in box or other containers"); ConnectionPool context = list.get(0); return context; } public static int localPoolSize() { List list = connectionHolder.get(); return list.size(); } public static void clossLeakConnection() { List list = connectionHolder.get(); if (list.isEmpty()) { connectionHolder.remove(); return; } try { LOGGER.error("### connection leak:" + list.size()); while (list.size() > 0) { list.get(0).close(); } connectionHolder.remove(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } public SumkConnection connection(DBType userType) throws SQLException { if (ActionContext.current().isTest()) { return this.getWriteConnection(); } switch (this.dbType) { case WRITE: if (userType == DBType.READONLY) { throw new SimpleSumkException(5639234, "can not open readOnly connection in write context"); } return this.getWriteConnection(); case READONLY: if (userType == DBType.WRITE) { throw new SimpleSumkException(5639234, "can not open write connection in readonly context"); } return this.getReadConnection(); case READ_PREFER: if (userType == DBType.WRITE) { return this.getWriteConnection(); } return this.getReadConnection(); default: return this.connectionByCommand(userType); } } private SumkConnection connectionByCommand(DBType type) throws SQLException { if (type == DBType.WRITE) { return this.getWriteConnection(); } if (type == DBType.ANY) { if (this.readConn != null) { return this.readConn; } if (this.writeConn != null) { return this.writeConn; } } return this.getReadConnection(); } private SumkConnection getReadConnection() throws SQLException { if (this.readConn != null) { return this.readConn; } SumkDataSource dataSource = DataSources.readDataSource(dbName); if (dataSource == null) { throw new SumkException(124234154, dbName + "没有可用的读数据源"); } if (this.writeConn != null && this.writeConn.dataSource == dataSource) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("{}写锁分身出读锁", this.dbName); } this.readConn = this.writeConn.copy(); return this.readConn; } this.readConn = dataSource.getConnection(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("open read connection:{}", readConn); } return readConn; } private SumkConnection getWriteConnection() throws SQLException { if (this.writeConn != null) { return this.writeConn; } SumkDataSource dataSource = DataSources.writeDataSource(dbName); if (dataSource == null) { throw new SumkException(124234153, dbName + "没有可用的写数据源"); } if (this.readConn != null && this.readConn.dataSource == dataSource) { this.writeConn = this.readConn.copy(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("{}读锁升级出写锁", this.dbName); } } else { this.writeConn = dataSource.getConnection(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("open write connection:{}", writeConn); } } this.writeConn.setAutoCommit(this.autoCommit); return writeConn; } private SumkDataSource getDataSource(SumkConnection c) { if (c == null) { return null; } return c.dataSource; } private void runHook(TxHook type, Throwable exception) { HookContext c = null; for (SqlSessionHook act : this.hooks) { if (act.getType() == type) { if (c == null) { c = new HookContext(getDataSource(this.writeConn), getDataSource(this.readConn), exception); } act.getAction().accept(c); } } } public void commit() throws SQLException { if (this.hooks != null) { runHook(TxHook.ON_COMMIT, null); } if (this.writeConn != null) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("commit {}", this.dbName); } this.eventLane.commit(this.writeConn); } if (this.hooks != null) { runHook(TxHook.COMMITED, null); } } public void rollback(Throwable e) throws SQLException { eventLane.clear(); if (this.writeConn != null) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("rollback {}", this.dbName); } this.writeConn.rollback(); } if (this.hooks != null) { runHook(TxHook.ROLLBACK, e); } } @Override public void close() throws Exception { eventLane.clear(); if (this.writeConn != null) { try { this.writeConn.close(); } catch (Exception e) { Log.printStack("sumk.sql.error", e); } } if (this.readConn != null) { try { this.readConn.close(); } catch (Exception e) { Log.printStack("sumk.sql.error", e); } } if (this.hooks != null) { runHook(TxHook.CLOSED, null); } this.writeConn = null; this.readConn = null; List list = connectionHolder.get(); int size = list.size(); list.remove(this); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Close connection context [{}], from size {} to {}", this.dbName, size, list.size()); } } public String getDbName() { return dbName; } public DBType getDbType() { return dbType; } public boolean isAutoCommit() { return autoCommit; } public void pubuishModify(DBEvent event) { if (this.writeConn != null) { this.eventLane.pubuishModify(this.writeConn, event); } } public boolean existModifyEvent(PojoMeta pm) { return this.eventLane.existModifyEvent(pm.getTableName()); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DBCPDataSourceFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.yx.common.util.kit.Asserts; import org.yx.conf.SimpleBeanUtil; import org.yx.exception.SumkException; import org.yx.log.Logs; public class DBCPDataSourceFactory implements DataSourceFactory { private static final Map DEFAULT_PROPERTIES = new HashMap<>(); static { DEFAULT_PROPERTIES.put("driverClassName", "com.mysql.jdbc.Driver"); DEFAULT_PROPERTIES.put("maxTotal", "50"); DEFAULT_PROPERTIES.put("minIdle", "5"); DEFAULT_PROPERTIES.put("maxIdle", "30"); DEFAULT_PROPERTIES.put("maxWaitMillis", "10000"); DEFAULT_PROPERTIES.put("testOnBorrow", "false"); DEFAULT_PROPERTIES.put("testOnReturn", "false"); DEFAULT_PROPERTIES.put("testWhileIdle", "true"); DEFAULT_PROPERTIES.put("removeAbandonedOnBorrow", "false"); DEFAULT_PROPERTIES.put("removeAbandonedOnMaintenance", "true"); DEFAULT_PROPERTIES.put("removeAbandonedTimeout", "300"); DEFAULT_PROPERTIES.put("logAbandoned", "true"); DEFAULT_PROPERTIES.put("timeBetweenEvictionRunsMillis", "30000"); DEFAULT_PROPERTIES.put("softMinEvictableIdleTimeMillis", "60000"); DEFAULT_PROPERTIES.put("logExpiredConnections", "false"); DEFAULT_PROPERTIES.put("poolPreparedStatements", "false"); } private boolean valid(Map properties) { return properties.get("url") != null && properties.get("username") != null && properties.get("password") != null; } @Override public DataSource create(Map properties, boolean readonly) { Asserts.requireTrue(this.valid(properties), "url,username,password should not be null"); BasicDataSource basic = new BasicDataSource(); try { Map map = new HashMap<>(DEFAULT_PROPERTIES); if (properties != null && properties.size() > 0) { map.putAll(properties); } SimpleBeanUtil.copyProperties(basic, map); basic.setDefaultReadOnly(readonly); } catch (Exception e) { Logs.db().error(e.getMessage(), e); throw new SumkException(23434, e.getMessage(), e); } return basic; } @Override public Map status(final DataSource datasource) { BasicDataSource ds = null; if (datasource instanceof BasicDataSource) { ds = (BasicDataSource) datasource; } else { try { ds = datasource.unwrap(BasicDataSource.class); } catch (Exception e) { Logs.db().error(e.getLocalizedMessage(), e); } } if (ds == null) { Logs.db().info("ds.class({}) is not instance from BasicDataSource", datasource.getClass().getName()); return Collections.emptyMap(); } Map map = new LinkedHashMap<>(); map.put("active", ds.getNumActive()); map.put("idle", ds.getNumIdle()); map.put("minIdle", ds.getMinIdle()); map.put("maxIdle", ds.getMaxIdle()); map.put("maxTotal", ds.getMaxTotal()); return map; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DBConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.db.enums.DBType; import org.yx.db.sql.DBSettings; import org.yx.exception.SumkException; import org.yx.log.Logs; public class DBConfig { private static Function passwordDecryptor = text -> { byte[] bs = S.base64().decode(text.getBytes()); try { return new String(S.cipher().decrypt(bs, DBSettings.getPasswordKey())); } catch (Exception e) { Logs.db().error("decrypt failed: " + text, e); throw new SumkException(2354223, e.getMessage(), e); } }; public static void setPasswordDecryptor(Function passwordDecryptor) { DBConfig.passwordDecryptor = Objects.requireNonNull(passwordDecryptor); } public static DBConfig create(String index, Map p) throws Exception { DBType type = DBType.ANY; int weight = 0, readWeight = 0; Map properties = new HashMap<>(); Set set = p.keySet(); for (String key : set) { String v = p.get(key); if (v == null || v.isEmpty()) { Logs.db().debug("db config {}={} is empty,ignore it.", key, v); continue; } switch (key.toLowerCase()) { case "type": type = DBConfig.parseFromConfigFile(v); break; case "weight": weight = Integer.parseInt(v); break; case "password": if (AppInfo.getBoolean("sumk.db.password.encry", false)) { v = passwordDecryptor.apply(v); } properties.put(key, v); break; case "read_weight": case "readweight": readWeight = Integer.parseInt(v); break; default: properties.put(key, v); break; } } return new DBConfig(index, type, weight, readWeight, properties); } private static DBType parseFromConfigFile(String type) { String type2 = type.toLowerCase(); switch (type2) { case "w": case "write": return DBType.WRITE; case "r": case "read": return DBType.READONLY; case "wr": case "rw": case "any": return DBType.ANY; default: throw new SumkException(2342312, type + " is not correct db type"); } } final DBType type; final int weight; final int readWeight; final String index; final Map properties; public DBConfig(String index, DBType type, int weight, int readWeight, Map properties) { this.index = index; this.type = type; this.weight = weight; this.readWeight = readWeight; this.properties = properties; } public String getProperty(String name) { return this.properties.get(name); } public Map getProperties() { return properties; } public String getIndex() { return index; } public DBType getType() { return type; } public int getWeight() { return weight; } public int getReadWeight() { return readWeight; } @Override public String toString() { return "DBConfig [type=" + type + ", weight=" + weight + ", readWeight=" + readWeight + ", index=" + index + ", properties=" + properties + "]"; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DBConfigFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.List; import org.yx.base.Ordered; public interface DBConfigFactory extends Ordered { List create(String dbName) throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DSFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Map; import java.util.Objects; import javax.sql.DataSource; import org.slf4j.Logger; import org.yx.db.enums.DBType; import org.yx.log.Logs; public final class DSFactory { private static DataSourceFactory factory; static { try { factory = new DBCPDataSourceFactory(); } catch (Throwable e) { Logger log = Logs.db(); if (log.isInfoEnabled()) { log.error("cannot create dbcp2 factory," + e, e); } else { log.error("cannot create dbcp2 factory"); } } } public static DataSourceFactory factory() { return factory; } public static void setFactory(DataSourceFactory factory) { DSFactory.factory = Objects.requireNonNull(factory); } public static SumkDataSource create(String name, String index, DBType type, Map properties) { DataSource basic = factory.create(properties, !type.isWritable()); Logs.db().debug("create ds: {}", basic); return new SumkDataSource(name, index, type, basic); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DataSourceFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Map; import javax.sql.DataSource; public interface DataSourceFactory { DataSource create(Map properties, boolean readonly); Map status(DataSource datasource); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DataSourceManager.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Set; public interface DataSourceManager { String status(); /** * 销毁mananger,不能抛出异常 */ void destroy(); SumkDataSource writeDataSource(); SumkDataSource readDataSource(); Set allDataSources(); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DataSourceManagerImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import org.yx.bean.IOC; import org.yx.common.route.Router; import org.yx.common.util.S; import org.yx.conf.Const; import org.yx.exception.SumkException; import org.yx.log.Logs; public final class DataSourceManagerImpl implements DataSourceManager { private Router read; private Router write; private final String db; public DataSourceManagerImpl(String dbName) { this.db = Objects.requireNonNull(dbName); try { this.parseDatasource(); } catch (Exception e) { Logs.db().error(e.getMessage(), e); throw new SumkException(1432543, dbName + "创建DataSourceManager" + "失败"); } } public DataSourceManagerImpl(String dbName, Router write, Router read) throws Exception { this.db = Objects.requireNonNull(dbName); this.write = write; this.read = read; } @Override public String status() { Map map = new TreeMap<>(); for (SumkDataSource datasource : this.allDataSources()) { map.put(datasource.toString(), datasource); } StringBuilder sb = new StringBuilder(100); for (SumkDataSource datasource : map.values()) { sb.append(datasource).append(" = ").append(S.json().toJson(datasource.status())).append(Const.LN); } return sb.toString(); } @Override public void destroy() { for (SumkDataSource ds : this.allDataSources()) { ds.close(); } } private void parseDatasource() throws Exception { if (this.write != null || this.read != null) { Logs.db().info("{} has inited datasource", this.db); return; } RouterFactory factory = IOC.get(RouterFactory.class); if (factory == null) { factory = new WeightedRouterFactory(); } List> container = factory.create(this.db); this.write = container.get(0); this.read = container.get(1); } @Override public String toString() { return "datasource[" + db + "] write=" + write + " ,read=" + read; } @Override public SumkDataSource writeDataSource() { return this.write.select(); } @Override public SumkDataSource readDataSource() { return this.read.select(); } @Override public Set allDataSources() { Set set = new HashSet<>(); set.addAll(this.read.allSources()); set.addAll(this.write.allSources()); return set; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DataSourceManagerSelector.java ================================================ package org.yx.db.conn; import java.util.Set; public interface DataSourceManagerSelector { DataSourceManager select(String dbName); /** * 已经加载的数据源名称 * * @return 数据源名称列表,不为null */ Set dbNames(); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DataSources.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Objects; public final class DataSources { private static DataSourceManagerSelector managerSelector = new DefaultManagerSelector(); public static void setManagerSelector(DataSourceManagerSelector selector) { DataSources.managerSelector = Objects.requireNonNull(selector); } public static DataSourceManagerSelector getManagerSelector() { return managerSelector; } public static SumkDataSource writeDataSource(String dbName) { return getManager(dbName).writeDataSource(); } public static SumkDataSource readDataSource(String dbName) { return getManager(dbName).readDataSource(); } public static DataSourceManager getManager(String dbName) { return managerSelector.select(dbName); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/DefaultManagerSelector.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.function.Function; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.StringUtil; public class DefaultManagerSelector implements DataSourceManagerSelector { /** * factoryMap只能重新赋值,不能修改 */ private Map factoryMap = Collections.emptyMap(); protected Map aliasMap; private Function managerFactory = DataSourceManagerImpl::new; public DefaultManagerSelector() { Map configMap = AppInfo.subMap("s.alias.db."); if (configMap.size() > 0) { Map temp = new HashMap<>(); for (Entry entry : configMap.entrySet()) { String name = entry.getKey(); String value = entry.getValue(); if (name.isEmpty() || StringUtil.isEmpty(value)) { continue; } List aliases = StringUtil.splitAndTrim(StringUtil.toLatin(value), Const.COMMA); for (String alias : aliases) { if (temp.putIfAbsent(alias, name) != null) { Logs.redis().error("redis别名{}重复了", name); } } } if (temp.size() > 0) { this.aliasMap = new HashMap<>(temp); } } } public void setManagerFactory(Function managerFactory) { this.managerFactory = Objects.requireNonNull(managerFactory); } public synchronized DataSourceManager addManagerIfAbsent(String dbName, DataSourceManager factory) { Map map = new HashMap<>(this.factoryMap); DataSourceManager pre = map.putIfAbsent(dbName, factory); if (pre == null) { this.factoryMap = map; } return pre; } public synchronized DataSourceManager removeManager(String dbName) throws Exception { Map map = new HashMap<>(this.factoryMap); DataSourceManager old = map.remove(dbName); if (old != null) { this.factoryMap = map; } return old; } protected String getDbName(String dbName) { if (this.aliasMap == null) { return dbName; } return aliasMap.getOrDefault(dbName, dbName); } @Override public DataSourceManager select(String dbName) { dbName = getDbName(dbName); DataSourceManager factory = factoryMap.get(dbName); if (factory != null) { return factory; } try { for (int i = 0; i < 3; i++) { synchronized (this) { if ((factory = factoryMap.get(dbName)) != null) { return factory; } factory = managerFactory.apply(dbName); if (factory == null) { continue; } Logs.db().info("create dataSource manager: {}", dbName); DataSourceManager pre = this.addManagerIfAbsent(dbName, factory); if (pre == null) { return factory; } Logs.db().info("create dataSource manager '{}' twice!!", dbName); factory.destroy(); return pre; } } throw new SumkException(100234321, "get DataSourceManager [" + dbName + "] failed"); } catch (Exception e) { throw new SumkException(100234325, "create DataSourceManager [" + dbName + "] failed", e); } } public Map aliasMap() { return new HashMap<>(aliasMap); } public Set dbNames() { return new HashSet<>(this.factoryMap.keySet()); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/HookContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; /** * 所有字段都有可能为null */ public class HookContext { private final SumkDataSource write; private final SumkDataSource read; private final Throwable exception; public HookContext(SumkDataSource write, SumkDataSource read, Throwable exception) { this.write = write; this.read = read; this.exception = exception; } public SumkDataSource getWrite() { return write; } public SumkDataSource getRead() { return read; } public Throwable getException() { return exception; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/RouterFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.List; import org.yx.common.route.Router; public interface RouterFactory { List> create(String dbName) throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/SqlSessionHook.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.Objects; import java.util.function.Consumer; import org.yx.db.enums.TxHook; class SqlSessionHook { private final TxHook type; private final Consumer action; public SqlSessionHook(TxHook type, Consumer action) { this.type = Objects.requireNonNull(type); this.action = Objects.requireNonNull(action); } public TxHook getType() { return type; } public Consumer getAction() { return action; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((action == null) ? 0 : action.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SqlSessionHook other = (SqlSessionHook) obj; if (action == null) { if (other.action != null) return false; } else if (!action.equals(other.action)) return false; if (type != other.type) return false; return true; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/SumkConnection.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; import org.yx.base.context.ActionContext; import org.yx.log.Log; import org.yx.log.Logs; public final class SumkConnection implements Connection { private Connection inner; final SumkDataSource dataSource; private final boolean originAutoCommit; private boolean autoCommit; @Override public boolean isReadOnly() throws SQLException { return inner.isReadOnly(); } public SumkConnection(Connection inner, SumkDataSource ds) { this.inner = inner; this.dataSource = ds; this.originAutoCommit = originAutoCommit(); } private boolean originAutoCommit() { try { this.autoCommit = inner.getAutoCommit(); return this.autoCommit; } catch (SQLException e) { Logs.db().error("获取原始的autoCommit失败", e); return true; } } public SumkDataSource dataSource() { return dataSource; } @Override public T unwrap(Class iface) throws SQLException { return inner.unwrap(iface); } @Override public boolean isWrapperFor(Class iface) throws SQLException { return inner.isWrapperFor(iface); } @Override public Statement createStatement() throws SQLException { Statement stmt = inner.createStatement(); return stmt; } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { PreparedStatement stmt = inner.prepareStatement(sql); return stmt; } @Override public CallableStatement prepareCall(String sql) throws SQLException { CallableStatement stmt = inner.prepareCall(sql); return stmt; } @Override public String nativeSQL(String sql) throws SQLException { return inner.nativeSQL(sql); } @Override public void setAutoCommit(boolean auto) throws SQLException { if (auto == this.autoCommit) { return; } inner.setAutoCommit(auto); this.autoCommit = auto; } @Override public boolean getAutoCommit() { return autoCommit; } @Override public void commit() throws SQLException { if (ActionContext.current().isTest()) { this.rollback(); return; } if (inner == null) { throw new SQLException("connection is closed"); } inner.commit(); } @Override public void rollback() throws SQLException { inner.rollback(); } @Override public void close() throws SQLException { if (this.inner == null || this.inner.isClosed()) { return; } if (Log.get("sumk.conn").isTraceEnabled()) { Log.get("sumk.conn").trace("close connection: " + this); } try { this.recoverAutoCommit(); } catch (Exception e) { Log.get("sumk.conn").error(e.getMessage(), e); } inner.close(); this.inner = null; } @Override public boolean isClosed() throws SQLException { if (this.inner == null) { return true; } return inner.isClosed(); } @Override public DatabaseMetaData getMetaData() throws SQLException { return inner.getMetaData(); } @Override public void setReadOnly(boolean readOnly) throws SQLException { inner.setReadOnly(readOnly); } @Override public void setCatalog(String catalog) throws SQLException { inner.setCatalog(catalog); } @Override public String getCatalog() throws SQLException { return inner.getCatalog(); } @Override public void setTransactionIsolation(int level) throws SQLException { inner.setTransactionIsolation(level); } @Override public int getTransactionIsolation() throws SQLException { return inner.getTransactionIsolation(); } @Override public SQLWarning getWarnings() throws SQLException { return inner.getWarnings(); } @Override public void clearWarnings() throws SQLException { inner.clearWarnings(); } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { Statement stmt = inner.createStatement(resultSetType, resultSetConcurrency); return stmt; } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { PreparedStatement stmt = inner.prepareStatement(sql, resultSetType, resultSetConcurrency); return stmt; } @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { CallableStatement stmt = inner.prepareCall(sql, resultSetType, resultSetConcurrency); return stmt; } @Override public Map> getTypeMap() throws SQLException { return inner.getTypeMap(); } @Override public void setTypeMap(Map> map) throws SQLException { inner.setTypeMap(map); } @Override public void setHoldability(int holdability) throws SQLException { inner.setHoldability(holdability); } @Override public int getHoldability() throws SQLException { return inner.getHoldability(); } @Override public Savepoint setSavepoint() throws SQLException { return inner.setSavepoint(); } @Override public Savepoint setSavepoint(String name) throws SQLException { return inner.setSavepoint(name); } @Override public void rollback(Savepoint savepoint) throws SQLException { inner.rollback(savepoint); } @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { inner.releaseSavepoint(savepoint); } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { Statement stmt = inner.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); return stmt; } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { PreparedStatement stmt = inner.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); return stmt; } @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { CallableStatement stmt = inner.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); return stmt; } @Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { PreparedStatement stmt = inner.prepareStatement(sql, autoGeneratedKeys); return stmt; } @Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { PreparedStatement stmt = inner.prepareStatement(sql, columnIndexes); return stmt; } @Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { PreparedStatement stmt = inner.prepareStatement(sql, columnNames); return stmt; } @Override public Clob createClob() throws SQLException { return inner.createClob(); } @Override public Blob createBlob() throws SQLException { return inner.createBlob(); } @Override public NClob createNClob() throws SQLException { return inner.createNClob(); } @Override public SQLXML createSQLXML() throws SQLException { return inner.createSQLXML(); } @Override public boolean isValid(int timeout) throws SQLException { return inner.isValid(timeout); } @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { inner.setClientInfo(name, value); } @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { inner.setClientInfo(properties); } @Override public String getClientInfo(String name) throws SQLException { return inner.getClientInfo(name); } @Override public Properties getClientInfo() throws SQLException { return inner.getClientInfo(); } @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return inner.createArrayOf(typeName, elements); } @Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return inner.createStruct(typeName, attributes); } @Override public void setSchema(String schema) throws SQLException { inner.setSchema(schema); } @Override public String getSchema() throws SQLException { return inner.getSchema(); } @Override public void abort(Executor executor) throws SQLException { inner.abort(executor); } @Override public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { inner.setNetworkTimeout(executor, milliseconds); } @Override public int getNetworkTimeout() throws SQLException { return inner.getNetworkTimeout(); } @Override public String toString() { return this.dataSource + "::" + String.valueOf(inner); } public boolean isSameInnerConnection(SumkConnection w) { return w != null && this.inner == w.inner && this.dataSource == w.dataSource; } public SumkConnection copy() { return new SumkConnection(this.inner, this.dataSource); } private void recoverAutoCommit() throws SQLException { if (originAutoCommit == this.getAutoCommit()) { return; } Log.get("sumk.conn").trace("recover autoCommit to {}", originAutoCommit); inner.setAutoCommit(originAutoCommit); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/SumkDataSource.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Logger; import javax.sql.DataSource; import org.yx.db.enums.DBType; import org.yx.exception.SumkException; import org.yx.log.Logs; public class SumkDataSource implements DataSource { private final String name; private final DBType type; private final DataSource proxy; private final String index; private boolean enable = true; private final AtomicLong openCounter = new AtomicLong(); public SumkDataSource(String name, String index, DBType type, DataSource ds) { this.name = Objects.requireNonNull(name); this.type = Objects.requireNonNull(type); this.proxy = Objects.requireNonNull(ds); this.index = index; } public long getOpenCount() { return openCounter.get(); } public String getIndex() { return index; } public String getName() { return name; } public DBType getType() { return type; } @Override public SumkConnection getConnection() throws SQLException { this.openCounter.incrementAndGet(); return new SumkConnection(proxy.getConnection(), this); } @Override public PrintWriter getLogWriter() throws SQLException { return proxy.getLogWriter(); } @Override public void setLogWriter(PrintWriter out) throws SQLException { proxy.setLogWriter(out); } @Override public void setLoginTimeout(int seconds) throws SQLException { proxy.setLoginTimeout(seconds); } @Override public int getLoginTimeout() throws SQLException { return proxy.getLoginTimeout(); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return proxy.getParentLogger(); } @Override public T unwrap(Class iface) throws SQLException { if (iface == this.getClass()) { return iface.cast(this); } if (iface.isInstance(proxy)) { return iface.cast(proxy); } throw new SumkException(234345, this.getClass().getSimpleName() + " does not wrap " + iface.getName()); } @Override public boolean isWrapperFor(Class iface) throws SQLException { return iface == this.getClass() || iface.isInstance(proxy); } @Override public String toString() { return new StringBuilder().append(name).append(".").append(index).toString(); } @Override public Connection getConnection(String username, String password) throws SQLException { this.openCounter.incrementAndGet(); return new SumkConnection(proxy.getConnection(username, password), this); } public boolean isEnable() { return enable; } public void setEnable(boolean enable) { this.enable = enable; } public boolean close() { if (!(this.proxy instanceof AutoCloseable)) { Logs.db().warn("DataSource {} unsupport close", proxy); return false; } try { AutoCloseable c = (AutoCloseable) proxy; Logs.db().info("DataSource {} begin closing...", c); c.close(); Logs.db().info("DataSource {} closed", c); return true; } catch (Throwable e) { Logs.db().error(e.getMessage(), e); return false; } } public Map status() { Map map = new LinkedHashMap<>(); Map inner = DSFactory.factory().status(proxy); if (inner != null) { map.putAll(inner); } map.put("openCount", getOpenCount()); return map; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/WeightedDataSource.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import org.yx.common.route.AbstractWeightedServer; public class WeightedDataSource extends AbstractWeightedServer { public WeightedDataSource(SumkDataSource source) { super(source); } @Override public boolean isEnable() { return this.source.isEnable(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/conn/WeightedRouterFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.conn; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.yx.bean.IOC; import org.yx.common.route.Router; import org.yx.common.route.Routes; import org.yx.common.route.WeightedServer; import org.yx.conf.AppInfo; import org.yx.db.enums.DBType; import org.yx.exception.SumkException; import org.yx.log.Logs; public class WeightedRouterFactory implements RouterFactory { @Override public List> create(String dbName) throws Exception { List configs = parseDBConfig(dbName); List> readDSList = new ArrayList<>(1); List> writeDSList = new ArrayList<>(1); for (DBConfig dc : configs) { SumkDataSource ds = DSFactory.create(dbName, dc.index, dc.type, dc.properties); if (ds.getType().isWritable()) { WeightedServer w = new WeightedDataSource(ds); w.setWeight(dc.getWeight() > 0 ? dc.getWeight() : 1); writeDSList.add(w); if (ds.getType() == DBType.ANY) { WeightedServer r = new WeightedDataSource(ds); r.setWeight(dc.getReadWeight() > 0 ? dc.getReadWeight() : 1); readDSList.add(r); } } else if (ds.getType().isReadable()) { WeightedServer r = new WeightedDataSource(ds); int w = dc.getReadWeight() > 0 ? dc.getReadWeight() : dc.getWeight(); r.setWeight(w > 0 ? w : 1); readDSList.add(r); } } Router read = createWeightedRouter(dbName, DBType.READ_PREFER, readDSList); Router write = createWeightedRouter(dbName, DBType.WRITE, writeDSList); return Arrays.asList(write, read); } protected Router createWeightedRouter(String name, DBType type, List> wds) { if (wds.isEmpty()) { if (AppInfo.getBoolean("sumk.db.empty.allow", false)) { Logs.db().warn("you have not config any read datasource for [{}]", name); } else { throw new SumkException(83587871, "you have not config " + type + " datasource for " + name); } } return Routes.createWeightedRouter(wds); } protected List parseDBConfig(String db) throws Exception { List factorys = IOC.getBeans(DBConfigFactory.class); for (DBConfigFactory factory : factorys) { List configs = factory.create(db); if (configs != null) { return configs; } } throw new SumkException(83587875, "no DBConfigFactory for " + db); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/dao/AbstractCachable.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.dao; import org.yx.db.sql.DBFlag; import org.yx.db.sql.DBSettings; import org.yx.util.BitUtil; public abstract class AbstractCachable { private boolean cacheEnable = true; public boolean isCacheEnable() { return cacheEnable && BitUtil.getBit(DBSettings.flag(), DBFlag.SELECT_FROM_CACHE); } protected void setCacheEnable(boolean cache) { this.cacheEnable = cache; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/dao/CountedResult.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.dao; import java.util.List; /** * 用来存放结果集以及总记录数 */ public class CountedResult { private List list; private long count; public CountedResult(List list, long count) { this.list = list; this.count = count; } /** * @return 当前页的结果集 */ public List getResult() { return list; } /** * @return 符合条件的总记录数 */ public long getCount() { return count; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/enums/CacheType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.enums; public enum CacheType { NOCACHE, SINGLE, LIST; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/enums/ColumnType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.enums; /** * 无论是数据库主键,还是redis主键,都不允许为null * * @author 游夏 * */ public enum ColumnType { NORMAL(false, false, 100), /** * 数据库主键。不允许为null。在更新的时候,如果没有显式设置where条件,主键字段将不会被更新。
*/ ID_DB(true, false, 1), /** * redis 主键,不允许为null */ ID_CACHE(false, true, 2), /** * 既是数据库主键,也是redis主键。不允许为null,不会被更新 */ ID_BOTH(true, true, 1); private final boolean dbID; private final boolean cacheID; private final int order; public int order() { return this.order; } public boolean isDbID() { return dbID; } public boolean isCacheID() { return cacheID; } private ColumnType(boolean dbId, boolean cacheId, int order) { this.dbID = dbId; this.cacheID = cacheId; this.order = order; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/enums/DBType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.enums; public enum DBType { WRITE(true, false), READ_PREFER(false, true), READONLY(false, true), ANY(true, true); private boolean writable; private boolean readable; private DBType(boolean writable, boolean readable) { this.writable = writable; this.readable = readable; } public boolean isWritable() { return writable; } public boolean isReadable() { return readable; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/enums/TransactionType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.enums; public enum TransactionType { /** * 如果没有事务,就开启新事物。如果已经存在,就啥都不做 */ REQUIRED, /** * 新事务 */ REQUIRES_NEW, AUTO_COMMIT; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/enums/TxHook.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.enums; /** * 触发钩子的情形有以下两种: *
    *
  1. 最外层的@Box方法执行结束,无论是否有实际操作数据库都会触发
  2. *
  3. 直接调用DB.commit()、DB.rollback()也会触发钩子
  4. *
*/ public enum TxHook { /** * 只有这个钩子可以操作数据库,但不要做太复杂的操作。 它在Const.LISTENER_DB_MODIFY_ON_COMMIT前执行 */ ON_COMMIT, /** * 它在Const.LISTENER_DB_MODIFY后执行 */ COMMITED, /** * 回滚之后执行 */ ROLLBACK, CLOSED } ================================================ FILE: sumk-db/src/main/java/org/yx/db/enums/ValidRecord.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.enums; public enum ValidRecord { /** * 在SoftDelete中,跟VALID相同的表示有效,其它都是无效 */ EQUAL_VALID, /** * 在SoftDelete中,只要跟INVALID不同都是有效 */ NOT_INVALID } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/DBEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.util.Objects; import org.yx.db.sql.PojoMeta; import org.yx.db.sql.PojoMetaHolder; public class DBEvent { private final String tableName; public DBEvent(String table) { this.tableName = Objects.requireNonNull(table); } public String getTable() { return this.tableName; } public PojoMeta getTableMeta() { return PojoMetaHolder.getTableMeta(this.tableName); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/DBEventPublisher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.util.List; import org.yx.bean.IOC; import org.yx.common.listener.EventBus; import org.yx.conf.Const; public final class DBEventPublisher { private static EventBus modifyBus; private static EventBus queryBus; private static EventBus onCommitBus; public static void init() { modifyBus = IOC.get(Const.LISTENER_DB_MODIFY, EventBus.class); queryBus = IOC.get(Const.LISTENER_DB_QUERY, EventBus.class); onCommitBus = IOC.get(Const.LISTENER_DB_MODIFY_ON_COMMIT, EventBus.class); } public static void onCommit(List events) { if (onCommitBus != null) { onCommitBus.publishBatch(events); } } public static void publishModify(List events) { modifyBus.publishBatch(events); } public static void publishModify(DBEvent event) { modifyBus.publish(event); } public static void publishQuery(QueryEvent event) { queryBus.publish(event); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/DeleteEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.util.List; import java.util.Map; public class DeleteEvent extends ModifyEvent { private List> wheres; public DeleteEvent(String table, int flag, List> wheres) { super(table, flag); this.wheres = wheres; } public List> getWheres() { return this.wheres; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/EventLane.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.yx.db.conn.SumkConnection; public final class EventLane { private List events; public EventLane() { } private List makeSureEvents() { if (this.events == null) { this.events = new ArrayList<>(); } return this.events; } public void pubuishModify(SumkConnection conn, DBEvent event) { if (event == null) { return; } if (!conn.getAutoCommit()) { makeSureEvents().add(event); } else { DBEventPublisher.publishModify(event); } } public void commit(SumkConnection conn) throws SQLException { if (this.events == null) { conn.commit(); return; } DBEventPublisher.onCommit(events); conn.commit(); DBEventPublisher.publishModify(events); this.clear(); } public void clear() { this.events = null; } public boolean existModifyEvent(String table) { if (this.events == null) { return false; } for (DBEvent event : this.events) { if (event.getTable().equals(table) && (event instanceof ModifyEvent)) { return true; } } return false; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/InsertEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.util.List; import java.util.Map; public class InsertEvent extends ModifyEvent { private List> pojos; public InsertEvent(String table, int flag, List> pojos) { super(table, flag); this.pojos = pojos; } public List> getPojos() { return pojos; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/ModifyEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import org.yx.exception.SumkException; import org.yx.util.BitUtil; public class ModifyEvent extends DBEvent { private int affected; protected int flag; public ModifyEvent(String table, int flag) { super(table); this.flag = flag; } public int getAffected() { return affected; } public void setAffected(int modified) { this.affected = modified; } public int flag() { return flag; } /** * 用于支持event之间传递boolean值,比如是否已经被处理了。调用本方法进行设置,调用getBoolean方法获取设置的结果 * 这个用于扩展,框架本身并没有用到 * * @param slot 29到32之间的一个数字,只要跟getBoolean里的位数能对上就行,无实际意义 * @param h 布尔值 */ public void setBoolean(int slot, boolean h) { if (slot < 29 || slot > 32) { throw new SumkException(346243451, "slot必须在29-32之间,实际却是:" + slot); } this.flag = BitUtil.setBit(flag, slot, h); } public boolean getBoolean(int slot) { return BitUtil.getBit(flag, slot); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/QueryEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.util.List; import java.util.Map; public class QueryEvent extends DBEvent { public QueryEvent(String table) { super(table); } List> in; List result; public List> getIn() { return in; } public void setIn(List> in) { this.in = in; } public List getResult() { return result; } public void setResult(List result) { this.result = result; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/event/UpdateEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.event; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.yx.annotation.doc.NotNull; import org.yx.base.sumk.map.ListMap; import org.yx.db.sql.DBFlag; import org.yx.db.sql.DBSettings; import org.yx.util.BitUtil; public class UpdateEvent extends ModifyEvent { private final Map to; private final Map incrMap; private final List> wheres; public UpdateEvent(String table, int flag, Map to, Map incrMap, List> wheres) { super(table, flag); this.incrMap = incrMap == null || incrMap.isEmpty() ? null : incrMap; this.wheres = wheres; this.to = isFullUpdate() ? to : removeNull(to); } public boolean isFullUpdate() { return BitUtil.getBit(flag, DBFlag.UPDATE_FULL_UPDATE); } public boolean isUpdateDBID() { return BitUtil.getBit(flag, DBFlag.UPDATE_UPDATE_DBID); } public Map getTo() { return to; } public List> getWheres() { return wheres; } public Map getIncrMap() { return incrMap; } public boolean canUpdateCache() { return wheres.size() == 1 && getAffected() == 1 && incrMap == null && isFullUpdate() && BitUtil.getBit(DBSettings.flag(), DBFlag.UPDATE_TO_CACHE); } private Map removeNull(@NotNull Map map) { Map ret = new ListMap<>(); for (Entry entry : map.entrySet()) { K k = entry.getKey(); V v = entry.getValue(); if (k == null || v == null) { continue; } ret.put(k, v); } return ret; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/exec/BoxAopExecutorSupplier.java ================================================ package org.yx.db.exec; import java.lang.reflect.Method; import org.yx.annotation.Bean; import org.yx.bean.aop.AopExecutor; import org.yx.bean.aop.AopExecutorSupplier; import org.yx.db.spec.BoxSpec; import org.yx.db.spec.DBSpecs; @Bean public class BoxAopExecutorSupplier implements AopExecutorSupplier { /** * 事务比一般的aop早启动,这样大多数的aop都在事务里执行 */ @Override public int order() { return 50; } private DBSource[] boxs = new DBSource[0]; private DBSource changeToDBSource(BoxSpec box) { for (DBSource tmp : boxs) { if (tmp.dbName().equals(box.value()) && tmp.dbType().equals(box.dbType()) && tmp.transactionType().equals(box.transaction())) { return tmp; } } int index = boxs.length; DBSource[] box2 = new DBSource[index + 1]; System.arraycopy(boxs, 0, box2, 0, boxs.length); DBSource db = new DefaultDBSource(box.value(), box.dbType(), box.transaction()); box2[index] = db; this.boxs = box2; return db; } @Override public synchronized DBSource willProxy(Class clz, Method rawMethod) { BoxSpec spec = DBSpecs.extractBox(rawMethod); if (spec == null) { return null; } return this.changeToDBSource(spec); } @Override public AopExecutor get(Object obj) { DBSource dbSource = (DBSource) obj; return new DBTransaction(dbSource); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/exec/DBExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.exec; @FunctionalInterface public interface DBExecutor { T execute(DBSource d) throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/exec/DBSource.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.exec; import org.yx.db.enums.DBType; import org.yx.db.enums.TransactionType; public interface DBSource { String dbName(); DBType dbType(); TransactionType transactionType(); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/exec/DBSources.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.exec; import org.yx.conf.Const; import org.yx.db.enums.DBType; import org.yx.db.enums.TransactionType; public final class DBSources { private static final DBSource WRITE = new DefaultDBSource(Const.DEFAULT_DB_NAME, DBType.WRITE, TransactionType.REQUIRED); private static final DBSource READONLY = new DefaultDBSource(Const.DEFAULT_DB_NAME, DBType.READONLY, TransactionType.REQUIRED); private static final DBSource ANY = new DefaultDBSource(Const.DEFAULT_DB_NAME, DBType.ANY, TransactionType.REQUIRED); public static DBSource write() { return WRITE; } public static DBSource readOnly() { return READONLY; } public static DBSource any() { return ANY; } public static DBSource write(String dbName) { return new DefaultDBSource(dbName, DBType.WRITE, TransactionType.REQUIRED); } public static DBSource readOnly(String dbName) { return new DefaultDBSource(dbName, DBType.READONLY, TransactionType.REQUIRED); } public static DBSource any(String dbName) { return new DefaultDBSource(dbName, DBType.ANY, TransactionType.REQUIRED); } public static DBSource writeAutoCommit(String dbName) { return new DefaultDBSource(dbName, DBType.WRITE, TransactionType.AUTO_COMMIT); } public static DBSource autoCommit(String dbName) { return new DefaultDBSource(dbName, DBType.ANY, TransactionType.AUTO_COMMIT); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/exec/DBTransaction.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.exec; import java.sql.SQLException; import org.yx.annotation.doc.NotNull; import org.yx.bean.aop.AopExecutor; import org.yx.db.conn.ConnectionPool; import org.yx.exception.SumkException; import org.yx.log.Log; public final class DBTransaction implements AopExecutor, AutoCloseable { private ConnectionPool dbCtx = null; @NotNull private final DBSource box; public DBTransaction(@NotNull DBSource box) { this.box = box; } public void begin() { switch (box.transactionType()) { case AUTO_COMMIT: dbCtx = ConnectionPool.create(box.dbName(), box.dbType(), true); return; case REQUIRES_NEW: dbCtx = ConnectionPool.create(box.dbName(), box.dbType(), false); return; case REQUIRED: dbCtx = ConnectionPool.createIfAbsent(box.dbName(), box.dbType()); return; default: return; } } public void rollback(Throwable e) { if (dbCtx == null || dbCtx.isAutoCommit()) { return; } try { dbCtx.rollback(e); } catch (SQLException e1) { Log.printStack("sumk.sql.error", e1); } } public void commit() { if (dbCtx == null || dbCtx.isAutoCommit()) { return; } try { this.dbCtx.commit(); } catch (SQLException e) { Log.printStack("sumk.sql.error", e); } } @Override public void close() { if (dbCtx == null) { return; } try { dbCtx.close(); } catch (Exception e) { throw new SumkException(7820198, "error in commit," + e.getMessage(), e); } } public ConnectionPool getConnectionPool() { return dbCtx; } public DBSource getDBSource() { return box; } @Override public void before(Object[] params) { this.begin(); } @Override public Throwable after(Object result, Throwable e, boolean methodExecuted) { try { if (e == null) { this.commit(); return null; } this.rollback(e); return e; } finally { this.close(); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/exec/DefaultDBSource.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.exec; import java.util.Objects; import org.yx.db.enums.DBType; import org.yx.db.enums.TransactionType; public class DefaultDBSource implements DBSource { private final String dbName; private final DBType type; private final TransactionType transactionType; public DefaultDBSource(String dbName, DBType type, TransactionType transactionType) { this.dbName = Objects.requireNonNull(dbName); this.type = Objects.requireNonNull(type); this.transactionType = Objects.requireNonNull(transactionType); } public static DefaultDBSource create(String dbName, DBType type, TransactionType transactionType) { return new DefaultDBSource(dbName, type, transactionType); } public String dbName() { return dbName; } public DBType dbType() { return type; } @Override public TransactionType transactionType() { return this.transactionType; } @Override public String toString() { return "DefaultDatabase [dbName=" + dbName + ", type=" + type + ", transactionType=" + transactionType + "]"; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/kit/DBKits.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.kit; import java.sql.PreparedStatement; import java.util.List; import java.util.Objects; import java.util.function.Function; import org.yx.db.sql.PojoMeta; import org.yx.db.sql.PojoMetaHolder; import org.yx.db.visit.RecordRepository; import org.yx.exception.SumkException; import org.yx.exception.SumkExceptionCode; import org.yx.util.StringUtil; public final class DBKits { private static Function plainSqlParse = statement -> { String sql = String.valueOf(statement); int index = sql.indexOf(": "); if (index < 10 || index + 10 > sql.length()) { return sql; } return sql.substring(index + 2); }; public static void setPlainSqlParse(Function plainSqlParse) { DBKits.plainSqlParse = Objects.requireNonNull(plainSqlParse); } public static String getSqlOfStatement(PreparedStatement statement) { return plainSqlParse.apply(statement); } public static T queryOne(List list) { if (list == null || list.isEmpty()) { return null; } if (list.size() > 1) { throw new SumkException(SumkExceptionCode.DB_TOO_MANY_RESULTS, "result size:" + list.size() + " more than one"); } return list.get(0); } @SafeVarargs public static int clearCache(T... pojos) throws Exception { int total = 0; if (pojos == null || pojos.length == 0) { return total; } T t = null; for (int i = 0; i < pojos.length; i++) { t = pojos[i]; if (t != null) { break; } } if (t == null) { return total; } PojoMeta pm = PojoMetaHolder.getPojoMeta(t.getClass(), null); if (pm == null || pm.isNoCache()) { return total; } for (T src : pojos) { if (src == null) { continue; } String id = pm.getCacheID(src, false); if (StringUtil.isEmpty(id)) { continue; } RecordRepository.del(pm, id); total++; } return total; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/kit/SDBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.kit; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.yx.common.util.S; import org.yx.db.SDB; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.Loader; public final class SDBuilder { private String name; private Map param; public SDBuilder name(String name) { this.name = name; return this; } @SuppressWarnings("unchecked") public SDBuilder param(Object p) { if (p == null) { this.param = null; return this; } if (p instanceof Map) { this.param = (Map) p; return this; } this.param = S.bean().beanToMap(p, false); return this; } public List list(Class clz) { List> list = SDB.list(name, param); if (list == null || list.isEmpty()) { return Collections.emptyList(); } List retList = new ArrayList<>(); for (Map ret : list) { retList.add(toBean(ret, clz)); } return retList; } public T queryOne(Class clz) { Map ret = SDB.queryOne(name, param); return toBean(ret, clz); } private T toBean(Map ret, Class clz) { if (ret == null) { return null; } return S.bean().fillBeanIgnoreCaseAndUnderLine(ret, newInstance(clz)); } private T newInstance(Class clz) { try { return Loader.newInstance(clz); } catch (Exception e) { Logs.db().error(e.toString(), e); throw new SumkException(234125435, "创建" + clz.getName() + "的实例失败,可能是它没有无参的构造函数"); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/listener/DeleteListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.listener; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import org.yx.annotation.Bean; import org.yx.common.listener.SumkListener; import org.yx.conf.Const; import org.yx.db.event.DeleteEvent; import org.yx.db.sql.DBSettings; import org.yx.db.sql.PojoMeta; import org.yx.db.visit.RecordRepository; @Bean public class DeleteListener implements SumkListener { @Override public int order() { return 92; } @Override public Collection acceptType() { return Collections.singletonList(Const.LISTENER_DB_MODIFY); } @Override public void listen(Object ev) throws Exception { if (!DBSettings.toCache() || !(ev instanceof DeleteEvent)) { return; } DeleteEvent event = (DeleteEvent) ev; PojoMeta pm = event.getTableMeta(); if (pm == null || pm.isNoCache()) { return; } List> wheres = event.getWheres(); if (wheres == null || wheres.isEmpty()) { return; } for (Map src : wheres) { String id = pm.getCacheID(src, true); RecordRepository.del(pm, id); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/listener/InsertListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.listener; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import org.yx.annotation.Bean; import org.yx.common.listener.SumkListener; import org.yx.conf.Const; import org.yx.db.DBJson; import org.yx.db.enums.CacheType; import org.yx.db.event.InsertEvent; import org.yx.db.sql.DBSettings; import org.yx.db.sql.PojoMeta; import org.yx.db.visit.RecordRepository; @Bean public class InsertListener implements SumkListener { @Override public int order() { return 90; } @Override public Collection acceptType() { return Collections.singletonList(Const.LISTENER_DB_MODIFY); } @Override public void listen(Object ev) throws Exception { if (!DBSettings.toCache() || !(ev instanceof InsertEvent)) { return; } InsertEvent event = (InsertEvent) ev; PojoMeta pm = event.getTableMeta(); List> list = event.getPojos(); if (pm == null || pm.isNoCache() || list == null) { return; } for (Map map : list) { String id = pm.getCacheID(map, false); if (id == null) { continue; } if (pm.cacheType() == CacheType.LIST) { RecordRepository.del(pm, id); return; } RecordRepository.set(pm, id, DBJson.operator().toJson(map)); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/listener/SelectListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.listener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import org.yx.annotation.Bean; import org.yx.common.listener.SumkListener; import org.yx.conf.Const; import org.yx.db.DBJson; import org.yx.db.conn.ConnectionPool; import org.yx.db.enums.CacheType; import org.yx.db.event.QueryEvent; import org.yx.db.sql.DBSettings; import org.yx.db.sql.PojoMeta; import org.yx.db.visit.RecordRepository; @Bean public class SelectListener implements SumkListener { @Override public Collection acceptType() { return Collections.singletonList(Const.LISTENER_DB_QUERY); } @Override public void listen(Object ev) throws Exception { if (!(ev instanceof QueryEvent)) { return; } QueryEvent event = (QueryEvent) ev; PojoMeta pm = event.getTableMeta(); if (pm == null || pm.isNoCache() || event.getResult() == null || ConnectionPool.get().existModifyEvent(pm)) { return; } List> in = event.getIn(); if (in == null) { return; } if (in.size() != 1) { if (pm.isPrimeKeySameWithCache() && pm.getDatabaseIds().size() == 1 && pm.cacheType() == CacheType.SINGLE) { this.singleIdCache(pm, event.getResult()); } return; } Map where = in.get(0); if (!pm.isOnlyCacheID(where)) { return; } String id = pm.getCacheID(where, false); if (id == null) { return; } List list = new ArrayList<>(4); for (Object obj : event.getResult()) { if (id.equals(pm.getCacheID(obj, false))) { list.add(obj); } } if (list.isEmpty()) { return; } if (pm.cacheType() == CacheType.LIST) { RecordRepository.set(pm, id, DBJson.operator().toJson(list)); return; } if (list.size() != 1 || list.get(0) == null) { return; } RecordRepository.set(pm, id, DBJson.operator().toJson(list.get(0))); } private void singleIdCache(PojoMeta pm, List result) throws Exception { int size = result.size(); int max = DBSettings.maxSingleKeyToCache(); if (size > max) { size = max; result = new ArrayList<>(result); Collections.shuffle(result, ThreadLocalRandom.current()); } for (int i = 0; i < size; i++) { Object obj = result.get(i); if (obj == null) { continue; } String id = pm.getCacheID(obj, false); if (id == null) { continue; } RecordRepository.set(pm, id, DBJson.operator().toJson(obj)); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/listener/UpdateListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.listener; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.yx.annotation.Bean; import org.yx.common.listener.SumkListener; import org.yx.conf.Const; import org.yx.db.DBJson; import org.yx.db.enums.CacheType; import org.yx.db.event.UpdateEvent; import org.yx.db.sql.ColumnMeta; import org.yx.db.sql.DBSettings; import org.yx.db.sql.PojoMeta; import org.yx.db.visit.RecordRepository; @Bean public class UpdateListener implements SumkListener { @Override public int order() { return 91; } @Override public Collection acceptType() { return Collections.singletonList(Const.LISTENER_DB_MODIFY); } @Override public void listen(Object ev) throws Exception { if (!DBSettings.toCache() || !(ev instanceof UpdateEvent)) { return; } UpdateEvent event = (UpdateEvent) ev; PojoMeta pm = event.getTableMeta(); if (pm == null || pm.isNoCache()) { return; } List> wheres = event.getWheres(); boolean canUpdateCache = event.canUpdateCache(); for (Map where : wheres) { handleUpdate(event, pm, where, canUpdateCache); } } private void handleUpdate(UpdateEvent event, PojoMeta pm, Map where, boolean canUpdateCache) throws Exception { String id = pm.getCacheID(where, true); Map to = new HashMap<>(event.getTo()); if (!event.isUpdateDBID()) { List m_ids = pm.getDatabaseIds(); if (m_ids != null && m_ids.size() > 0) { for (ColumnMeta m : m_ids) { String name = m.getFieldName(); Object v = where.get(name); if (v != null) { to.put(name, v); } else { to.remove(name); } } } } if (canUpdateCache) { String id_new = pm.getCacheID(to, true); if (!id.equals(id_new)) { RecordRepository.del(pm, id); } if (pm.cacheType() == CacheType.LIST || event.getIncrMap() != null) { RecordRepository.del(pm, id_new); } else { RecordRepository.set(pm, id_new, DBJson.operator().toJson(to)); } return; } RecordRepository.del(pm, id); Map where2 = new HashMap<>(where); where2.putAll(to); String id_new = pm.getCacheID(where2, true); if (id.equals(id_new)) { return; } RecordRepository.del(pm, id_new); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/log/SimpleSqlLogImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.log; import java.util.Objects; import org.yx.base.context.ActionContext; import org.yx.base.sumk.UnsafeStringWriter; import org.yx.db.DBJson; import org.yx.db.sql.DBSettings; import org.yx.db.sql.MapedSql; import org.yx.db.sql.SqlLog; import org.yx.db.visit.SumkStatement; import org.yx.log.Log; import org.yx.log.LogKits; import org.yx.log.LogLevel; import org.yx.log.Logs; import org.yx.log.impl.CodeLine; import org.yx.log.impl.CodeLineKit; import org.yx.log.impl.LogObject; import org.yx.log.impl.UnionLog; import org.yx.log.impl.UnionLogs; import org.yx.util.SumkDate; import com.google.gson.stream.JsonWriter; public class SimpleSqlLogImpl implements SqlLog { private static String LOG_NAME_SQL = "sumk.unionlog.sql"; public SimpleSqlLogImpl() { UnionLog union = UnionLogs.getUnionLog(); Logs.db().debug("sqlLog初始化时,UnionLog的状态为:{}", union.isStarted()); } public static String getSqlLogName() { return LOG_NAME_SQL; } public static void setSqlLogName(String sqlLogName) { sqlLogName = Objects.requireNonNull(sqlLogName).trim(); if (sqlLogName.length() > 0) { LOG_NAME_SQL = sqlLogName; } } @Override public void log(SumkStatement state, int totalTime, Throwable ex) { UnionLog union = UnionLogs.getUnionLog(); if (!union.isStarted()) { return; } try { MapedSql msql = state.getMaped(); UnsafeStringWriter stringWriter = new UnsafeStringWriter(64); JsonWriter writer = new JsonWriter(stringWriter); writer.setSerializeNulls(true); writer.beginObject(); writer.name("sql").value(msql.getSql()); writer.name("hash").value(msql.getSql().hashCode() & Integer.MAX_VALUE); String params = DBJson.operator().toJson(msql.getParamters()); params = LogKits.shorterSubfix(params, DBSettings.maxSqlParamLength()); writer.name("paramters").value(params); writer.name("sqlTime").value(state.getSqlTime()); if (state.getModifyCount() > -1) { writer.name("modifyCount").value(state.getModifyCount()); } if (totalTime > -1 && totalTime != state.getSqlTime()) { writer.name("totalTime").value(totalTime); } writer.endObject(); writer.flush(); writer.close(); CodeLine codeLine = CodeLineKit.parse(SumkStatement.getMarker(), LOG_NAME_SQL); LogLevel methodLevel = ex == null ? LogLevel.INFO : LogLevel.ERROR; LogObject logObj = new LogObject(LOG_NAME_SQL, SumkDate.now(), methodLevel, stringWriter.toString(), ex, Thread.currentThread().getName(), ActionContext.current().logContext(), codeLine); union.directOffer(logObj); } catch (Exception e) { Log.get("sumk.log").error(e.getMessage(), e); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/DBPlugin.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.yx.annotation.Bean; import org.yx.bean.Plugin; import org.yx.common.monitor.Monitors; import org.yx.conf.AppInfo; import org.yx.conf.MultiResourceLoader; import org.yx.db.conn.DataSources; import org.yx.db.event.DBEventPublisher; import org.yx.db.log.SimpleSqlLogImpl; import org.yx.db.monitor.DBMonitor; import org.yx.db.sql.DBSettings; import org.yx.db.visit.SumkStatement; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.util.SumkDate; @Bean public class DBPlugin implements Plugin { @Override public int order() { return 90; } @Override public void prepare() { DBSettings.init(); DBEventPublisher.init(); loadSDBResources(); try { SumkStatement.setSqlLog(new SimpleSqlLogImpl()); } catch (Throwable e) { Logs.db().warn("因为没有使用async-logger,所以不加载数据库的统一日志适配器"); } Monitors.add(new DBMonitor()); Logs.db().info("数据库插件启动完成"); } @Override public void startAsync() { preHotDataSource(); } protected void preHotDataSource() { if (AppInfo.getBoolean("sumk.db.pool.prehot.disable", false)) { return; } Map map = AppInfo.subMap("s.db."); if (map == null || map.isEmpty()) { return; } for (String key : map.keySet()) { int index = key.indexOf('.'); if (index > 0) { key = key.substring(0, index); } Logs.db().debug("{} begin preHot...", key); DataSources.getManager(key); } } protected void loadSDBResources() { try { MultiResourceLoader loader = SqlHolder.resourceLoader().get(); loadSql(loader); startListen(loader); } catch (Throwable e) { throw new SumkException(2351343, "sdb加载本地sql文件失败", e); } } private void startListen(MultiResourceLoader loader) { loader.startListen(load -> { try { Logs.db().info("local sql changed at {}", SumkDate.now().to_yyyy_MM_dd_HH_mm_ss()); loadSql(load); } catch (Exception e) { Log.printStack("sumk.sql.error", e); } }); } private void loadSql(MultiResourceLoader loader) throws Exception { Map inputMap = loader.openResources(null); if (inputMap == null || inputMap.isEmpty()) { return; } Map sqlMap = new HashMap<>(); try { for (Entry entry : inputMap.entrySet()) { String fileName = entry.getKey(); byte[] bs = entry.getValue(); if (bs == null || bs.length == 0) { continue; } SqlXmlParser.parseXml(sqlMap, SqlHolder.documentBuilderFactory().get(), fileName, new ByteArrayInputStream(bs)); } } catch (Exception e) { Logs.db().error(e.getLocalizedMessage(), e); return; } SqlHolder.setSQLS(sqlMap); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/ForeachParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Function; import org.yx.db.sql.MapedSql; import org.yx.db.sql.MapedSqlBuilder; import org.yx.exception.SumkException; import org.yx.log.Logs; public class ForeachParser implements SqlParser { private final JoinerFactory joinFactory; private final String itemName; private final String collecitonName; /** * #{@} 是列表中值的占位符 */ private final String template; public static ForeachParser create(String collection, String itemName, String template, JoinerFactory joinFactory) { if (template == null) { return null; } if (itemName != null) { itemName = itemName.trim(); if (itemName.isEmpty()) { itemName = null; } } return new ForeachParser(collection, itemName, template, joinFactory); } protected ForeachParser(String collecitonName, String itemName, String template, JoinerFactory joinFactory) { this.collecitonName = Objects.requireNonNull(collecitonName); this.itemName = itemName; this.template = Objects.requireNonNull(template).trim(); this.joinFactory = Objects.requireNonNull(joinFactory); } @SuppressWarnings("unchecked") @Override public MapedSql toMapedSql(Map param) throws Exception { Object obj = param.get(collecitonName); if (obj == null) { if (Logs.db().isTraceEnabled()) { Logs.db().trace("{} is null", collecitonName); } return null; } if (obj instanceof Collection) { Collection list = (Collection) obj; List mapeds = new ArrayList<>(list.size()); MapedSqlBuilder builder; for (Object v : list) { if (this.itemName == null) { builder = new MapedSqlBuilder(template, param); } else if (v instanceof Map) { builder = new MapedSqlBuilder(template, new ComposeMapHandler(param, (Map) v, this.itemName + ".")); } else { builder = new MapedSqlBuilder(template, new ComposeValueHandler(param, v, this.itemName)); } MapedSql maped = builder.toMapedSql(); if (maped != null) { mapeds.add(maped); } } if (mapeds.isEmpty()) { return null; } return MapedSql.merge(mapeds, joinFactory.create()); } throw new SumkException(235345346, "field " + collecitonName + " is not a Collection instance,it's type is " + obj.getClass().getSimpleName()); } @Override public String toString() { return "Foreach [colleciton=" + collecitonName + ", item=" + itemName + ", joinFactory=" + joinFactory + " : " + template + "]"; } private static class ComposeMapHandler implements Function { private final Map globalMap; private final Map itemMap; private final String prefix; public ComposeMapHandler(Map globalMap, Map itemMap, String prefix) { this.globalMap = globalMap; this.itemMap = itemMap; this.prefix = prefix; } @Override public Object apply(String key) { if (key.startsWith(this.prefix)) { return this.itemMap.get(key.substring(this.prefix.length())); } return this.globalMap.get(key); } } private static class ComposeValueHandler implements Function { private final Map globalMap; private final Object item; private final String itemName; public ComposeValueHandler(Map globalMap, Object item, String itemName) { this.globalMap = globalMap; this.item = item; this.itemName = itemName; } @Override public Object apply(String key) { if (key.equals(itemName)) { return item; } return this.globalMap.get(key); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/IFParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import org.yx.db.sql.MapedSql; public class IFParser implements SqlParser { private final SqlParser parser; private final Predicate> expression; public static IFParser create(Predicate> expression, SqlParser parser) { if (parser == null) { return null; } return new IFParser(expression, parser); } protected IFParser(Predicate> expression, SqlParser parser) { this.expression = Objects.requireNonNull(expression); this.parser = Objects.requireNonNull(parser); } @Override public MapedSql toMapedSql(Map map) throws Exception { return expression.test(map) ? parser.toMapedSql(map) : null; } @Override public String toString() { return "IF(" + expression + "): " + this.parser; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/ItemsParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import org.yx.db.sql.MapedSql; public class ItemsParser implements SqlParser { private final JoinerFactory joinFactory; private final SqlParser[] parsers; public static ItemsParser create(List list, JoinerFactory joinFactory) { if (list == null || list.isEmpty()) { return null; } return new ItemsParser(list.toArray(new SqlParser[list.size()]), joinFactory); } protected ItemsParser(SqlParser[] parsers, JoinerFactory joinFactory) { this.parsers = Objects.requireNonNull(parsers); this.joinFactory = Objects.requireNonNull(joinFactory); } @Override public MapedSql toMapedSql(Map param) throws Exception { List list = new ArrayList<>(this.parsers.length); for (SqlParser p : this.parsers) { MapedSql ms = p.toMapedSql(param); if (ms == null) { continue; } list.add(ms); } if (list.isEmpty()) { return null; } return MapedSql.merge(list, this.joinFactory.create()); } @Override public String toString() { return "ItemsParser [joinFactory=" + joinFactory + ", parsers=" + Arrays.toString(parsers) + "]"; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/JoinerFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import org.yx.base.ItemJoiner; public class JoinerFactory { private final CharSequence delimiter; private final CharSequence prefix; private final CharSequence suffix; public static JoinerFactory create(String delimiter, String prefix, String suffix) { delimiter = addBlank(delimiter); return new JoinerFactory(delimiter == null ? " " : delimiter, addBlank(prefix), addBlank(suffix)); } private static String addBlank(String p) { if (p == null) { return p; } p = p.trim(); if (p.isEmpty()) { return null; } return " " + p + " "; } private JoinerFactory(String delimiter, String prefix, String suffix) { this.delimiter = delimiter; this.prefix = prefix; this.suffix = suffix; } public ItemJoiner create() { return new ItemJoiner(delimiter, prefix, suffix); } @Override public String toString() { return "{" + prefix + " - " + delimiter + " - " + suffix + "}"; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/NamedExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.yx.db.sql.InsertResult; import org.yx.db.sql.MapedSql; import org.yx.db.sql.SqlBuilder; import org.yx.db.visit.Visitors; import org.yx.exception.SumkException; /** * 这里的sql,占位符不是?,而是#{**},里面的name,与map的key要一一对应
* * @author 游夏 * */ public final class NamedExecutor { private static int toSqlCount; public static int getExecuteCount() { return toSqlCount; } private static class InnerSqlBuilder implements SqlBuilder { private final Map map; private final SqlParser sqlParser; InnerSqlBuilder(SqlParser sql, Map map) { this.sqlParser = sql; this.map = map == null ? Collections.emptyMap() : new HashMap<>(map); } @Override public MapedSql toMapedSql() throws Exception { toSqlCount++; return sqlParser.toMapedSql(map); } } private static InnerSqlBuilder createSqlBuilder(SqlParser sql, Map map) { return new InnerSqlBuilder(sql, map); } public static int execute(SqlParser sql, Map map) { try { return Visitors.modifyVisitor.visit(createSqlBuilder(sql, map)); } catch (Exception e) { throw SumkException.wrap(e); } } public static InsertResult insertWithAutoGeneratedKeys(SqlParser sql, Map map) { try { return Visitors.insertWithAutoGeneratedKeysVisitor.visit(createSqlBuilder(sql, map)); } catch (Exception e) { throw SumkException.wrap(e); } } public static List> list(SqlParser sql, Map map) { try { return Visitors.queryVisitor.visit(createSqlBuilder(sql, map)); } catch (Exception e) { throw SumkException.wrap(e); } } public static List listInArray(SqlParser sql, Map map) { try { return Visitors.arrayListQueryVisitor.visit(createSqlBuilder(sql, map)); } catch (Exception e) { throw SumkException.wrap(e); } } public static List singleColumnList(SqlParser sql, Map map) { try { return Visitors.singleListQueryVisitor.visit(createSqlBuilder(sql, map)); } catch (Exception e) { throw SumkException.wrap(e); } } public static long count(SqlParser sql, Map map) { try { List list = Visitors.singleListQueryVisitor.visit(createSqlBuilder(sql, map)); Number n = (Number) list.get(0); return n.longValue(); } catch (Exception e) { throw SumkException.wrap(e); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/PureStringParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.Map; import org.yx.db.sql.MapedSql; import org.yx.db.sql.MapedSqlBuilder; public class PureStringParser implements SqlParser { private final String sql; public static PureStringParser create(String sql) { if (sql == null || sql.isEmpty()) { return null; } return new PureStringParser(sql); } private PureStringParser(String sql) { this.sql = sql.trim(); } @Override public MapedSql toMapedSql(Map map) throws Exception { return new MapedSqlBuilder(sql, map).toMapedSql(); } @Override public String toString() { return sql; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/RawExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.List; import java.util.Map; import org.yx.db.sql.InsertResult; import org.yx.db.sql.RawSqlBuilder; import org.yx.db.visit.Visitors; import org.yx.exception.SumkException; /** * 以?为占位符的原生sql。推荐通过常量或其它方式引用
* sql的参数支持以List的方式传入 * * @author 游夏 * */ public class RawExecutor { /** * @param sql 以?为占位符的原生sql。 * @param params 参数 * @return sql执行结果 */ public static int execute(String sql, Object... params) { try { return Visitors.modifyVisitor.visit(new RawSqlBuilder(sql, params)); } catch (Exception e) { throw SumkException.wrap(e); } } /** * 这个方法用于需要获取自增长主键的值的情况 * * @param sql 以?为占位符的原生sql。 * @param params 参数 * @return sql执行结果 */ public static InsertResult insertWithAutoGeneratedKeys(String sql, Object... params) { try { return Visitors.insertWithAutoGeneratedKeysVisitor.visit(new RawSqlBuilder(sql, params)); } catch (Exception e) { throw SumkException.wrap(e); } } /** * @param sql 以?为占位符的原生sql * @param params 参数 * @return 结果集 */ public static List> list(String sql, Object... params) { try { return Visitors.queryVisitor.visit(new RawSqlBuilder(sql, params)); } catch (Exception e) { throw SumkException.wrap(e); } } /** * @param sql 以?为占位符的原生sql * @param params 参数 * @return 结果集 */ public static List listInOrder(String sql, Object... params) { try { return Visitors.arrayListQueryVisitor.visit(new RawSqlBuilder(sql, params)); } catch (Exception e) { throw SumkException.wrap(e); } } /** * 只有一个列的list方法
* sum等函数是特殊的singleColumnList,它返回的list的size为1 * * @param sql 以?为占位符的原生sql * @param params 参数 * @return 结果集 */ public static List singleColumnList(String sql, Object... params) { try { return Visitors.singleListQueryVisitor.visit(new RawSqlBuilder(sql, params)); } catch (Exception e) { throw SumkException.wrap(e); } } /** * @param sql 以?为占位符的原生sql * @param params 参数 * @return 记录数 */ public static long count(String sql, Object... params) { try { List list = Visitors.singleListQueryVisitor.visit(new RawSqlBuilder(sql, params)); Number n = (Number) list.get(0); return n.longValue(); } catch (Exception e) { throw SumkException.wrap(e); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/SqlHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; import javax.xml.parsers.DocumentBuilder; import org.yx.conf.AppInfo; import org.yx.conf.LocalMultiResourceLoaderSupplier; import org.yx.conf.MultiResourceLoader; import org.yx.exception.SumkException; public class SqlHolder { private static Map SQLS = new HashMap<>(); public static void setSQLS(Map sqls) { SQLS = sqls; } private static Supplier documentBuilderFactory = new SqlXmlBuilderFactory(); private static Supplier resourceLoader = new LocalMultiResourceLoaderSupplier( AppInfo.get("sumk.db.sql.path", AppInfo.CLASSPATH_URL_PREFIX + "sql")); public static Supplier resourceLoader() { return resourceLoader; } public static void resourceLoader(Supplier resourceLoader) { SqlHolder.resourceLoader = Objects.requireNonNull(resourceLoader); } public static Supplier documentBuilderFactory() { return documentBuilderFactory; } public static void documentBuilderFactory(Supplier documentBuilderFactory) { SqlHolder.documentBuilderFactory = Objects.requireNonNull(documentBuilderFactory); } public static SqlParser findSql(String name) { SqlParser sql = SQLS.get(name); if (sql == null) { throw new SumkException(64342451, "sql [" + name + "] can not found "); } return sql; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/SqlParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.Map; import org.yx.db.sql.MapedSql; public interface SqlParser { MapedSql toMapedSql(Map param) throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/SqlParsers.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Predicate; import org.yx.common.expression.Expressions; import org.yx.common.expression.SimpleExpression; import org.yx.exception.SumkException; import org.yx.util.StringUtil; public final class SqlParsers { private static final String SPLIT_AND = ","; private static final String SPLIT_OR = "\\|"; public static Predicate> createParamExpression(String test, String matchType) { if (test == null || test.isEmpty()) { throw new SumkException(34645465, "if中的test不能为空"); } test = StringUtil.toLatin(test.trim()); boolean and = test.contains(SPLIT_AND); boolean or = test.contains("|"); if (and && or) { throw new SumkException(34645465, "if的test不能同时出现,和|"); } return createParamExpression(test, matchType, !or); } private static Predicate> createParamExpression(String test, String matchType, boolean and) { String[] ss = and ? test.split(SPLIT_AND) : test.split(SPLIT_OR); List>> list = new ArrayList<>(ss.length); for (String s : ss) { s = s.trim(); if (s.isEmpty()) { continue; } SimpleExpression exp = Expressions.createSimpleExpression(s, matchType); if (!list.contains(exp)) { list.add(exp); } } return Expressions.booleanExpression(list, and); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/SqlXmlBuilderFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.io.InputStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.function.Supplier; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.xml.sax.InputSource; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.IOUtil; public class SqlXmlBuilderFactory implements Supplier { protected final DocumentBuilderFactory dbf; protected String sumkDTD; public SqlXmlBuilderFactory() { dbf = DocumentBuilderFactory.newInstance(); dbf.setIgnoringComments(true); dbf.setIgnoringElementContentWhitespace(true); dbf.setExpandEntityReferences(false); dbf.setCoalescing(true); } private DocumentBuilder create() throws Exception { DocumentBuilder dbd = dbf.newDocumentBuilder(); dbd.setEntityResolver((publicId, systemId) -> { if (systemId == null) { return null; } String dtd = this.sumkDTD; if (dtd == null) { InputStream in = SqlXmlParser.class.getClassLoader().getResourceAsStream("META-INF/sql.dtd"); if (in == null) { return null; } byte[] bs = IOUtil.readAllBytes(in, true); if (bs == null || bs.length == 0) { return null; } dtd = new String(bs, StandardCharsets.UTF_8); this.sumkDTD = dtd; } InputSource source = new InputSource(systemId); source.setCharacterStream(new StringReader(dtd)); source.setEncoding("UTF-8"); return source; }); return dbd; } @Override public DocumentBuilder get() { try { return this.create(); } catch (Exception e) { Logs.db().error(e.toString(), e); throw new SumkException(7623435, "创建XmlDocumentBuilder失败"); } } public DocumentBuilderFactory getDocumentBuilderFactory() { return dbf; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/mapper/SqlXmlParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.mapper; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Predicate; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.yx.common.expression.MatchType; import org.yx.exception.SumkException; public class SqlXmlParser { private static final String ID = "id"; public static void parseXml(Map map, DocumentBuilder dbd, String fileName, InputStream in) throws Exception { Document doc = dbd.parse(in); Element root = doc.getDocumentElement(); String namespace = root.getAttribute("namespace"); if (namespace != null) { namespace = namespace.trim(); if (namespace.isEmpty()) { namespace = null; } } NodeList sqlNodeList = root.getChildNodes(); if (sqlNodeList == null || sqlNodeList.getLength() == 0) { return; } int len = sqlNodeList.getLength(); Node tmp; for (int i = 0; i < len; i++) { tmp = sqlNodeList.item(i); if (!Element.class.isInstance(tmp)) { continue; } Element el = (Element) tmp; if (!el.hasChildNodes()) { continue; } SqlParser parser = compose(parseSqlNode(el.getChildNodes())); if (parser != null) { if (map.putIfAbsent(name(namespace, el.getAttribute(ID)), parser) != null) { throw new SumkException(1435436, fileName + "-" + name(namespace, el.getAttribute(ID)) + " is duplicate"); } } } } private static String name(String namespace, String id) { if (namespace == null) { return id; } return String.join(".", namespace, id); } private static void add(List list, SqlParser parser) { if (parser != null) { list.add(parser); } } private static Predicate> paramExpression(Element el) { return SqlParsers.createParamExpression(el.getAttribute("test"), MatchType.matchTypeOrDefault(el.getAttribute("falseby"))); } private static SqlParser compose(List list) { if (list == null || list.isEmpty()) { return null; } if (list.size() == 1) { return list.get(0); } return ItemsParser.create(list, JoinerFactory.create(null, null, null)); } private static List parseSqlNode(NodeList sqlNodeList) throws SAXException, IOException, ParserConfigurationException { if (sqlNodeList == null) { return null; } int len = sqlNodeList.getLength(); if (len == 0) { return null; } List list = new ArrayList<>(len); Node tmp; for (int i = 0; i < len; i++) { tmp = sqlNodeList.item(i); if (tmp.getNodeType() == Node.TEXT_NODE) { String sql = tmp.getTextContent(); if (sql == null || (sql = sql.trim()).isEmpty()) { continue; } add(list, PureStringParser.create(sql)); continue; } if (!Element.class.isInstance(tmp)) { continue; } Element el = (Element) tmp; switch (el.getTagName()) { case "if": add(list, IFParser.create(paramExpression(el), compose(parseSqlNode(el.getChildNodes())))); break; case "ifnot": add(list, IFParser.create(paramExpression(el).negate(), compose(parseSqlNode(el.getChildNodes())))); break; case "items": add(list, items(el)); break; case "foreach": add(list, forEach(el)); break; default: throw new SumkException(1874546534, el + "不是有效的tag"); } } return list; } private static SqlParser forEach(Element el) { String collection = el.getAttribute("collection"); String itemName = el.getAttribute("item"); String template = el.getTextContent(); JoinerFactory joiner = JoinerFactory.create(el.getAttribute("separator"), el.getAttribute("open"), el.getAttribute("close")); return ForeachParser.create(collection, itemName, template, joiner); } private static SqlParser items(Element el) throws SAXException, IOException, ParserConfigurationException { JoinerFactory joiner = JoinerFactory.create(el.getAttribute("separator"), el.getAttribute("open"), el.getAttribute("close")); List list = parseSqlNode(el.getChildNodes()); return ItemsParser.create(list, joiner); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/monitor/DBMonitor.java ================================================ package org.yx.db.monitor; import static org.yx.common.monitor.Monitors.BLANK; import static org.yx.conf.AppInfo.LN; import java.util.List; import org.yx.common.monitor.MessageProvider; import org.yx.conf.AppInfo; import org.yx.db.conn.DataSourceManager; import org.yx.db.conn.DataSources; import org.yx.db.mapper.NamedExecutor; import org.yx.db.sql.PojoMeta; import org.yx.db.sql.PojoMetaHolder; import org.yx.db.sql.VisitCounter; import org.yx.db.visit.SumkStatement; import org.yx.util.StringUtil; public class DBMonitor implements MessageProvider { public String dbVisitInfo() { List list = PojoMetaHolder.allPojoMeta(); StringBuilder sb = new StringBuilder(128); sb.append("##sql总执行次数").append(BLANK).append(SumkStatement.getExecuteCount()).append(BLANK).append(BLANK) .append("SDB执行次数").append(BLANK).append(NamedExecutor.getExecuteCount()).append(LN); sb.append("#tableName").append(BLANK).append("queryCount").append(BLANK).append("modifyCount").append(BLANK) .append("cacheKeyVisits").append(BLANK).append("cacheKeyHits").append(AppInfo.LN); long modify, query, cacheVisit, cacheHit; long totalModify = 0, totalQuery = 0, totalCacheVisit = 0, totalCacheHit = 0; for (PojoMeta p : list) { VisitCounter c = p.getCounter(); query = c.getQueryCount(); modify = c.getModifyCount(); sb.append(p.getTableName()).append(BLANK).append(query).append(BLANK).append(modify).append(BLANK); totalModify += modify; totalQuery += query; if (p.isNoCache()) { sb.append("-").append(BLANK).append("-").append(AppInfo.LN); continue; } cacheVisit = c.getCacheKeyVisits(); cacheHit = c.getCacheKeyHits(); sb.append(cacheVisit).append(BLANK).append(cacheHit).append(AppInfo.LN); totalCacheVisit += cacheVisit; totalCacheHit += cacheHit; } sb.append("*total*").append(BLANK).append(totalQuery).append(BLANK).append(totalModify).append(BLANK) .append(totalCacheVisit).append(BLANK).append(totalCacheHit).append(AppInfo.LN); return sb.toString(); } public String dataSourceStatus(String name) { if (StringUtil.isEmpty(name)) { return null; } StringBuilder sb = new StringBuilder(200); if (!DataSources.getManagerSelector().dbNames().contains(name)) { sb.append(DataSources.getManagerSelector().dbNames()); return sb.toString(); } DataSourceManager manager = DataSources.getManager(name); if (manager == null) { return sb.toString(); } sb.append(manager.status()); return sb.toString(); } @Override public Object get(String type, String key, Object param) { if ("monitor".equals(type)) { if ("db.cache".equals(key)) { return this.dbVisitInfo(); } if ("datasource".equals(key)) { return this.dataSourceStatus((String) param); } } return null; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/spec/BoxSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.spec; import java.util.Objects; import org.yx.db.enums.DBType; import org.yx.db.enums.TransactionType; public class BoxSpec { private final String value; private final DBType dbType; private final TransactionType transaction; public BoxSpec(String value, DBType dbType, TransactionType transaction) { this.value = Objects.requireNonNull(value); this.dbType = Objects.requireNonNull(dbType); this.transaction = Objects.requireNonNull(transaction); } /** * @return 数据库字段的名字,不填的话,就是属性名(小写) */ public String value() { return this.value; } public DBType dbType() { return this.dbType; } public TransactionType transaction() { return this.transaction; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/spec/ColumnSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.spec; import java.util.Objects; import org.yx.db.enums.ColumnType; public class ColumnSpec { private final String value; private final ColumnType type; private final byte order; public ColumnSpec(String value, ColumnType type, byte order) { this.value = value; this.type = Objects.requireNonNull(type); this.order = order; } /** * @return 数据库字段的名字,不填的话,就是属性名(小写) */ public String value() { return this.value; } public ColumnType type() { return this.type; } public byte order() { return this.order; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/spec/DBSpecs.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.spec; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import org.yx.annotation.db.Box; import org.yx.annotation.db.Column; import org.yx.annotation.db.Table; import org.yx.annotation.spec.Specs; public class DBSpecs extends Specs { private static Function, TableSpec> tableParser = clz -> { Table table = clz.getAnnotation(Table.class); if (table == null) { return null; } return new TableSpec(table.value(), table.duration(), table.preInCache(), table.maxHit(), table.cacheType()); }; private static BiFunction, Field, ColumnSpec> columnParser = (clz, f) -> { Column c = f.getAnnotation(Column.class); if (c == null) { return null; } return new ColumnSpec(c.value(), c.type(), c.order()); }; private static Function boxParser = m -> { Box c = m.getAnnotation(Box.class); if (c == null) { return null; } return new BoxSpec(c.value(), c.dbType(), c.transaction()); }; public static TableSpec extractTable(Class clz) { return parse(clz, tableParser); } public static ColumnSpec extractColumn(Class clz, Field f) { return parse(clz, f, columnParser); } public static BoxSpec extractBox(Method m) { return parse(m, boxParser); } public static void setBoxParser(Function boxParser) { DBSpecs.boxParser = Objects.requireNonNull(boxParser); } public static void setColumnParser(BiFunction, Field, ColumnSpec> columnParser) { DBSpecs.columnParser = Objects.requireNonNull(columnParser); } public static void setTableParser(Function, TableSpec> tableParser) { DBSpecs.tableParser = Objects.requireNonNull(tableParser); } public static Function, TableSpec> getTableParser() { return tableParser; } public static BiFunction, Field, ColumnSpec> getColumnParser() { return columnParser; } public static Function getBoxParser() { return boxParser; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/spec/TableSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.spec; import java.util.Objects; import org.yx.db.enums.CacheType; public class TableSpec { private final String value; private final int duration; private final String preInCache; private final int maxHit; private final CacheType cacheType; public TableSpec(String value, int duration, String preInCache, int maxHit, CacheType cacheType) { this.value = value; this.duration = duration; this.preInCache = preInCache; this.maxHit = maxHit; this.cacheType = Objects.requireNonNull(cacheType); } /** * @return 表名。为空时,就是表名,支持#或者?作为通配符 */ public String value() { return value; } /** * @return 在缓存中保留的时间,单位秒。0表示使用全局设置,小于0表示不过期 */ public int duration() { return duration; } /** * 如果使用cluster,同一张表的DB缓存会在一个slot上,这是为了防止mget、mset出问题 * * @return 为空使用表名,一般使用默认就好。支持#或者?作为通配符 */ public String preInCache() { return preInCache; } /** * @return 访问多少次之后刷新缓存,0表示使用全局默认,小于0表示不刷新 */ public int maxHit() { return maxHit; } /** * @return 主键缓存都是SINGLE,外键缓存一般用LIST */ public CacheType cacheType() { return cacheType; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/AbstractOperationGroup.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.yx.base.ItemJoiner; public abstract class AbstractOperationGroup implements CompareOperation { protected List compare; protected void addOperation(CompareOperation op) { if (op == null) { return; } if (this.compare == null) { this.compare = new ArrayList<>(6); } this.compare.add(op); } public boolean isEmpty() { return compare == null || compare.isEmpty(); } public int size() { return compare == null ? 0 : compare.size(); } CompareOperation get(int index) { return this.compare.get(index); } public void removeCompares(String key, Operation op) { if (this.isEmpty()) { return; } Iterator it = this.compare.iterator(); while (it.hasNext()) { CompareOperation op0 = it.next(); if (!(op0 instanceof ColumnOperation)) { continue; } ColumnOperation cp = (ColumnOperation) op0; if (key != null && !key.equals(cp.getName())) { continue; } if (op != null && cp.getType() != op) { continue; } it.remove(); } } protected CharSequence buildSql(SelectBuilder select, List paramters, String split) { if (this.isEmpty()) { return ""; } ItemJoiner joiner = ItemJoiner.create(split, " ( ", " ) "); for (CompareOperation op : this.compare) { CharSequence cs = op.buildSql(select, paramters); if (cs.length() > 0) { joiner.item().append(cs); } } return joiner.toCharSequence(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/AbstractSqlBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import org.yx.base.sumk.map.ListMap; import org.yx.db.visit.SumkDbVisitor; import org.yx.exception.SumkException; import org.yx.util.BitUtil; import org.yx.util.CollectionUtil; public abstract class AbstractSqlBuilder implements SqlBuilder { protected SumkDbVisitor visitor; protected Class tableClass; protected PojoMeta pojoMeta; protected List> in; protected int flag; protected String sub; protected void sub(String sub) { this.sub = sub; if (this.pojoMeta != null) { this.pojoMeta = PojoMetaHolder.getPojoMeta(this.pojoMeta, sub); } } protected void checkMap(Map map, PojoMeta pm) { if (!this.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) { return; } Set keys = map.keySet(); for (String key : keys) { if (pm.getByFieldName(key) == null) { throw new SumkException(743257, key + " is not a field in " + pm.pojoClz.getName()); } } } protected void failIfNotAllowPropertyMiss(String key) { if (!this.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) { return; } String msg = this.pojoMeta == null ? key + " is not a field" : key + " is not a field in " + pojoMeta.pojoClz.getName(); throw new SumkException(743257, msg); } protected void checkIn() { if (CollectionUtil.isEmpty(this.in)) { throw new SumkException(7345245, "no conditions"); } } protected void _addIn(Object src) { this._addInByMap(this.populate(src, false)); } @SuppressWarnings("unchecked") protected Map populate(Object src, boolean keepNull) { if (src == null) { return Collections.emptyMap(); } if (src instanceof Map) { return new ListMap<>((Map) src); } if (this.tableClass == null) { this.tableClass = src.getClass(); } try { return this.makeSurePojoMeta().populate(src, keepNull); } catch (Exception e) { throw new SumkException(42534254, e.getMessage(), e); } } protected void _addInByMap(Map map) { if (map == null || map.isEmpty()) { return; } if (this.in == null) { this.in = new ArrayList<>(); } this.in.add(map); } public PojoMeta makeSurePojoMeta() { if (this.pojoMeta != null) { return this.pojoMeta; } if (this.tableClass != null) { this.pojoMeta = PojoMetaHolder.getPojoMeta(tableClass, sub); } if (this.pojoMeta == null) { throw new SumkException(7325435, "please call tableClass(XX.class) first"); } return this.pojoMeta; } public AbstractSqlBuilder(SumkDbVisitor visitor) { this.visitor = visitor; this.flag = DBSettings.flag(); } public final boolean isOn(int flagBit) { return BitUtil.getBit(this.flag, flagBit); } protected final void setOnOff(int flagBit, boolean onOff) { this.flag = BitUtil.setBit(this.flag, flagBit, onOff); } public int flag() { return this.flag; } protected T accept(SumkDbVisitor visitor) { try { return visitor.visit(this); } catch (Exception e) { if (e instanceof SumkException) { throw (SumkException) e; } throw new SumkException(53172189, e.getMessage(), e); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/ColumnMeta.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.lang.reflect.Field; import java.util.Map; import org.yx.annotation.doc.Comment; import org.yx.base.Ordered; import org.yx.common.util.kit.TypeConverter; import org.yx.db.enums.ColumnType; import org.yx.db.spec.ColumnSpec; import org.yx.util.StringUtil; public final class ColumnMeta implements Comparable { final Field field; final ColumnType meta; final byte columnOrder; final String dbColumn; public ColumnMeta(Field field, ColumnSpec c) { this.field = field; this.meta = c == null ? ColumnType.NORMAL : c.type(); if (c == null) { this.columnOrder = Ordered.DEFAULT_ORDER; } else { this.columnOrder = c.order(); } this.dbColumn = (c == null || StringUtil.isEmpty(c.value())) ? DBNameResolvers.getColumnNameResolver().apply(field.getName()) : c.value(); } public boolean isDBID() { return this.meta.isDbID(); } public boolean isCacheID() { return this.meta.isCacheID(); } public Object value(Object owner) throws IllegalArgumentException, IllegalAccessException { if (owner instanceof Map) { @SuppressWarnings("unchecked") Map map = (Map) owner; return map.get(field.getName()); } return field.get(owner); } public void setValue(Object owner, final Object value) throws Exception { if (owner instanceof Map) { @SuppressWarnings("unchecked") Map map = (Map) owner; map.put(field.getName(), value); return; } field.set(owner, TypeConverter.convert(value, field.getType())); } public String getFieldName() { return field.getName(); } @Override public int compareTo(ColumnMeta o) { if (this.columnOrder == o.columnOrder) { return Integer.compare(this.meta.order(), o.meta.order()); } return this.columnOrder > o.columnOrder ? 1 : -1; } @Override public String toString() { return "ColumnMeta [field=" + field.getName() + ", meta=" + meta + ", columnOrder=" + columnOrder + ", dbColumn=" + dbColumn + "]"; } public Field getField() { return field; } public ColumnType getMeta() { return meta; } public int getColumnOrder() { return columnOrder; } public String getDbColumn() { return dbColumn; } public String getComment() { Comment c = this.field.getAnnotation(Comment.class); return c == null ? "" : c.value(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/ColumnOperation.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.Collection; import java.util.List; import java.util.Objects; import org.yx.exception.SumkException; public class ColumnOperation implements CompareOperation { private final String name; private final Operation type; private final Object value; public ColumnOperation(String name, Operation type, Object value) { this.name = Objects.requireNonNull(name); this.type = Objects.requireNonNull(type); this.value = value; } public String getName() { return name; } public Operation getType() { return type; } public Object getValue() { return value; } @Override public String toString() { return name + type.op + "?"; } public boolean isSameOperation(ColumnOperation b) { return this.name.equals(b.name) && this.type == b.type; } @Override public CharSequence buildSql(SelectBuilder select, List paramters) { ColumnMeta cm = select.pojoMeta.getByFieldName(name); if (cm == null) { if (select.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) { throw new SumkException(54675234, name + "这个字段没有在" + select.pojoMeta.pojoClz.getName() + "中定义"); } return ""; } StringBuilder sb = new StringBuilder().append(cm.dbColumn); if (value == null) { if (type == Operation.EQUAL) { return sb.append(" IS NULL"); } if (type == Operation.NOT) { return sb.append(" IS NOT NULL"); } if (!select.isOn(DBFlag.SELECT_COMPARE_ALLOW_NULL)) { throw new SumkException(2342423, name + "的值为null"); } if (select.isOn(DBFlag.SELECT_COMPARE_IGNORE_NULL)) { return ""; } } sb.append(type.op); if (type == Operation.IN || type == Operation.NOT_IN) { sb.append("("); boolean first = true; for (Object obj : (Collection) value) { paramters.add(obj); if (first) { sb.append('?'); first = false; } else { sb.append(",?"); } } return sb.append(")"); } paramters.add(value); return sb.append('?'); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/CompareOperation.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.List; public interface CompareOperation { /** * 生成sql及参数,如果条件为空就返回长度为0的CharSequence,这时候不能对paramters参数有所修改。 允许抛出异常 * * @param paramters 解析出来的参数要按顺序放在这里。所以这个接口需要按顺序调用 * @return 使用过占位符的sql,返回值不为null。 */ CharSequence buildSql(SelectBuilder select, List paramters); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/Count.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.yx.db.mapper.RawExecutor; import org.yx.util.StringUtil; /** * 直接调用select.count()就可以了,一般不需要显式使用这个类 * * @see select */ public class Count { protected final SelectBuilder select; public Count(SelectBuilder select) { this.select = select; } public long execute() { List paramters = new ArrayList<>(8); PojoMeta pojoMeta = select.makeSurePojoMeta(); pojoMeta.getCounter().incrQueryCount(); Objects.requireNonNull(pojoMeta, "pojo meta cannot be null"); StringBuilder sql = new StringBuilder(); sql.append("SELECT count(*) FROM ").append(pojoMeta.getTableName()); CharSequence where = select.buildWhere(paramters); if (StringUtil.isNotEmpty(where)) { sql.append(" WHERE ").append(where); } return RawExecutor.count(sql.toString(), paramters); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/DBFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.Objects; public final class DBFactory { private static DBSupplier supplier = new DBSupplierImpl(); public static void setSupplier(DBSupplier supplier) { DBFactory.supplier = Objects.requireNonNull(supplier); } public static Select select() { return supplier.select(); } public static Insert insert() { return supplier.insert(); } public static Update update() { return supplier.update(); } public static Delete delete() { return supplier.delete(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/DBFlag.java ================================================ package org.yx.db.sql; public class DBFlag { public static final int FAIL_IF_PROPERTY_NOT_MAPPED = 1; public static final int SELECT_FROM_CACHE = 5; public static final int SELECT_TO_CACHE = 6; public static final int SELECT_ALLOW_EMPTY_WHERE = 7; public static final int SELECT_COMPARE_ALLOW_NULL = 8; public static final int SELECT_COMPARE_IGNORE_NULL = 9; public static final int SELECT_IGNORE_MAX_OFFSET = 10; public static final int SELECT_IGNORE_MAX_LIMIT = 11; public static final int UPDATE_UPDATE_DBID = 21; public static final int UPDATE_FULL_UPDATE = 22; /** * 如果是fullUpdate,并且其它条件也ok。就使用更新缓存的方式,而不是将缓存删除 */ public static final int UPDATE_TO_CACHE = 23; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/DBNameResolvers.java ================================================ package org.yx.db.sql; import java.util.Objects; import java.util.function.UnaryOperator; import org.yx.conf.AppInfo; import org.yx.util.StringUtil; public class DBNameResolvers { private static final UnaryOperator DEFAULT_RESOLVER = s -> { if (AppInfo.getBoolean("sumk.db.name.lowercase", false)) { return StringUtil.camelToUnderline(s).toLowerCase(); } return StringUtil.camelToUnderline(s); }; private static UnaryOperator columnNameResolver = DEFAULT_RESOLVER; private static UnaryOperator tableNameResolver = DEFAULT_RESOLVER; private static UnaryOperator cachePrefixResolver = tableName -> "{" + tableName + "}"; public static UnaryOperator getColumnNameResolver() { return columnNameResolver; } public static void setColumnNameResolver(UnaryOperator columnNameResolver) { DBNameResolvers.columnNameResolver = Objects.requireNonNull(columnNameResolver); } public static UnaryOperator getTableNameResolver() { return tableNameResolver; } public static void setTableNameResolver(UnaryOperator tableNameResolver) { DBNameResolvers.tableNameResolver = Objects.requireNonNull(tableNameResolver); } public static UnaryOperator getCachePrefixResolver() { return cachePrefixResolver; } public static void setCachePrefixResolver(UnaryOperator cachePrefixResolver) { DBNameResolvers.cachePrefixResolver = Objects.requireNonNull(cachePrefixResolver); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/DBSettings.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.Objects; import java.util.function.IntFunction; import org.yx.conf.AppInfo; import org.yx.db.enums.DBType; import org.yx.log.Logs; import org.yx.util.BitUtil; public final class DBSettings { private static boolean TO_CACHE; private static int FLAG; private static int MAX_LOG_PARAM_LENGTH; private static int MAX_SINGLEKEY_TO_CACHE; private static int MAX_LIMIT; private static int MAX_OFFSET; private static int UNION_LOG_TIME; private static boolean UNION_LOG_ENABLE; private static int DEBUG_LOG_SPEND_TIME; private static byte[] PASSWORD_KEY = new byte[] { 121, 111, 117, 116, 111, 110, 103, 108, 117, 97, 110, 64, 115, 117, 109, 107 }; private static DBType readType; private static int MAX_QUERY_CACHE_SIZE; private static SoftDeleteParser softDeleteParser = new SoftDeleteParserImpl(); private static IntFunction visitCounterFactory; public static int flag() { return FLAG; } public static IntFunction visitCounterFactory() { return visitCounterFactory; } public static void setVisitCounterFactory(IntFunction factory) { DBSettings.visitCounterFactory = factory; } public static SoftDeleteParser softDeleteParser() { return softDeleteParser; } public static void setSoftDeleteParser(SoftDeleteParser parser) { DBSettings.softDeleteParser = parser; } public static int maxQueryCacheSize() { return MAX_QUERY_CACHE_SIZE; } public static DBType readType() { return readType; } public static int maxSingleKeyToCache() { return MAX_SINGLEKEY_TO_CACHE; } public static byte[] getPasswordKey() { return PASSWORD_KEY; } public static void setPasswordKey(byte[] passwordKey) { PASSWORD_KEY = Objects.requireNonNull(passwordKey); } public static int debugLogSpendTime() { return DEBUG_LOG_SPEND_TIME; } public static int unionLogTime() { return UNION_LOG_TIME; } public static boolean isUnionLogEnable() { return UNION_LOG_ENABLE; } public static boolean toCache() { return TO_CACHE; } public static int MaxLimit() { return MAX_LIMIT; } public static int MaxOffset() { return MAX_OFFSET; } public static int maxSqlParamLength() { return MAX_LOG_PARAM_LENGTH; } public static synchronized void init() { if (DBSettings.readType != null || MAX_LIMIT > 0) { return; } DBSettings.FLAG = BitUtil.setBit(DBSettings.FLAG, DBFlag.UPDATE_FULL_UPDATE, AppInfo.getBoolean("sumk.db.update.full", false)); DBSettings.FLAG = BitUtil.setBit(DBSettings.FLAG, DBFlag.UPDATE_UPDATE_DBID, AppInfo.getBoolean("sumk.db.update.dbid", false)); AppInfo.addObserver(info -> { try { TO_CACHE = AppInfo.getBoolean("sumk.db.toCache", true); MAX_LIMIT = AppInfo.getInt("sumk.db.select.max.limit", 10000); MAX_OFFSET = AppInfo.getInt("sumk.db.select.max.offset", 10000); MAX_LOG_PARAM_LENGTH = AppInfo.getInt("sumk.sql.param.maxlength", 5000); UNION_LOG_TIME = AppInfo.getInt("sumk.unionlog.sql.time", 0); UNION_LOG_ENABLE = AppInfo.getBoolean("sumk.unionlog.sql.enable", true); DEBUG_LOG_SPEND_TIME = AppInfo.getInt("sumk.sql.debug.spendTime", 100); MAX_QUERY_CACHE_SIZE = AppInfo.getInt("sumk.select.query.maxsize", 1000); MAX_SINGLEKEY_TO_CACHE = AppInfo.getInt("sumk.select.singlekey.max.tocache", 10); int flag = DBSettings.FLAG; flag = BitUtil.setBit(flag, DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, AppInfo.getBoolean("sumk.db.failIfPropertyNotMapped", true)); flag = BitUtil.setBit(flag, DBFlag.SELECT_FROM_CACHE, AppInfo.getBoolean("sumk.db.fromCache", true)); flag = BitUtil.setBit(flag, DBFlag.SELECT_TO_CACHE, TO_CACHE && AppInfo.getBoolean("sumk.db.select.toCache", true)); flag = BitUtil.setBit(flag, DBFlag.SELECT_COMPARE_IGNORE_NULL, AppInfo.getBoolean("sumk.db.select.compare.null.ignore", true)); flag = BitUtil.setBit(flag, DBFlag.SELECT_COMPARE_ALLOW_NULL, AppInfo.getBoolean("sumk.db.select.compare.null.allow", true)); flag = BitUtil.setBit(flag, DBFlag.SELECT_IGNORE_MAX_LIMIT, AppInfo.getBoolean("sumk.db.select.ignore.maxlimit", false)); flag = BitUtil.setBit(flag, DBFlag.SELECT_IGNORE_MAX_OFFSET, AppInfo.getBoolean("sumk.db.select.ignore.maxoffset", false)); flag = BitUtil.setBit(flag, DBFlag.SELECT_ALLOW_EMPTY_WHERE, AppInfo.getBoolean("sumk.db.select.emptywhere", false)); flag = BitUtil.setBit(flag, DBFlag.UPDATE_TO_CACHE, AppInfo.getBoolean("sumk.db.update.toCache", true)); DBSettings.FLAG = flag; Logs.db().info("flag : {}", Integer.toHexString(DBSettings.FLAG)); } catch (Exception e) { Logs.db().error(e.getMessage(), e); } String read = AppInfo.get("sumk.db.readtype", null); if (read != null) { try { DBSettings.readType = DBType.valueOf(read.toUpperCase()); } catch (Exception e) { Logs.db().error("sumk.db.readtype配置不正确," + e.getMessage(), e); } } else { DBSettings.readType = DBType.ANY; } }); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/DBSupplier.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; public interface DBSupplier { Insert insert(); Update update(); Select select(); Delete delete(); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/DBSupplierImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import org.yx.db.visit.Visitors; public class DBSupplierImpl implements DBSupplier { @Override public Insert insert() { return new Insert(Visitors.modifyVisitor); } @Override public Update update() { return new Update(Visitors.modifyVisitor); } @Override public Delete delete() { return new Delete(Visitors.modifyVisitor); } @Override public Select select() { return new Select(Visitors.queryVisitorForORM); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/Delete.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import org.yx.db.visit.SumkDbVisitor; public class Delete extends ModifySqlBuilder { public Delete(SumkDbVisitor visitor) { super(visitor); } public Delete failIfPropertyNotMapped(boolean onOff) { this.setOnOff(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, onOff); return this; } /** * 删除的条件。如果是map类型,就要设置tableClass
* 多次执行delete,相互之间是or条件。
* 注意:如果pojo是map类型,那么它的null值是有效条件 * * @param pojo map或pojo * @return 当前对象 */ public Delete delete(Object pojo) { this._addIn(pojo); return this; } public Delete tableClass(Class tableClass) { this.tableClass = tableClass; return this; } /** * 分表的情况下,设置分区名。这个方法只能调用一次 * * @param sub 分区名 * @return 当前对象 */ public Delete partition(String sub) { sub(sub); return this; } public MapedSql toMapedSql() throws InstantiationException, IllegalAccessException { this.checkIn(); if (this.pojoMeta.softDelete == null) { return new HardDelete(this).toMapedSql(); } return new SoftDelete(this).toMapedSql(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/EstimateVisitCounter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import org.yx.conf.AppInfo; public class EstimateVisitCounter implements VisitCounter { private int visitCount; private int cacheMeet; private int modifyCount; private final int interval; private int queryCount; private int willVisit; public EstimateVisitCounter(int interval) { this.interval = interval > 0 ? interval : AppInfo.getInt("sumk.cache.count", 500); this.willVisit = this.interval; } @Override public long getCacheKeyVisits() { return Integer.toUnsignedLong(visitCount); } @Override public long getCacheKeyHits() { return Integer.toUnsignedLong(cacheMeet); } @Override public boolean willVisitCache(int c) { if (--willVisit <= 0) { this.willVisit = this.interval; return false; } visitCount += c; return true; } @Override public void incrCacheHit(int c) { cacheMeet += c; } @Override public long getModifyCount() { return Integer.toUnsignedLong(this.modifyCount); } @Override public void incrModifyCount() { this.modifyCount++; } @Override public long getQueryCount() { return Integer.toUnsignedLong(this.queryCount); } @Override public void incrQueryCount() { this.queryCount++; } @Override public int getInterval() { return this.interval; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/GroupAND.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.List; import org.yx.util.CollectionUtil; public class GroupAND extends AbstractOperationGroup { public static GroupAND create() { return new GroupAND(); } public GroupAND and(String name, Operation type, Object value) { return and(new ColumnOperation(name, type, value)); } public GroupAND and(CompareOperation op) { this.addOperation(op); return this; } @Override public CharSequence buildSql(SelectBuilder select, List paramters) { return this.buildSql(select, paramters, " AND "); } GroupAND unmodifyFirstLevel() { if (this.compare != null) { this.compare = CollectionUtil.unmodifyList(this.compare); } return this; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/GroupOR.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.List; public class GroupOR extends AbstractOperationGroup { public static GroupOR create() { return new GroupOR(); } public GroupOR or(String name, Operation type, Object value) { return or(new ColumnOperation(name, type, value)); } public GroupOR or(CompareOperation op) { this.addOperation(op); return this; } @Override public CharSequence buildSql(SelectBuilder select, List paramters) { return this.buildSql(select, paramters, " OR "); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/InnerDelete.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.Map; import java.util.Map.Entry; import org.yx.base.ItemJoiner; import org.yx.db.event.DeleteEvent; public abstract class InnerDelete { protected Delete delete; public InnerDelete(Delete delete) { this.delete = delete; } protected MapedSql toMapedSql(StringBuilder sb, MapedSql ms) throws InstantiationException, IllegalAccessException { PojoMeta pojoMeta = delete.makeSurePojoMeta(); sb.append(" WHERE "); ItemJoiner orItem = new ItemJoiner(" OR ", null, null); for (Map oneWhere : delete.in) { delete.checkMap(oneWhere, pojoMeta); ItemJoiner andItem = new ItemJoiner(" AND ", " ( ", " ) "); for (Entry en : oneWhere.entrySet()) { ColumnMeta fm = pojoMeta.getByFieldName(en.getKey()); if (fm == null) { delete.failIfNotAllowPropertyMiss(en.getKey()); continue; } Object value = en.getValue(); if (value == null) { andItem.item().append(fm.dbColumn).append(" IS NULL"); continue; } andItem.item().append(fm.dbColumn).append(" = ?"); ms.addParam(value); } orItem.item().append(andItem.toCharSequence()); } sb.append(orItem.toCharSequence()); ms.sql = sb.toString(); ms.event = new DeleteEvent(pojoMeta.getTableName(), delete.flag(), delete.in); return ms; } } class HardDelete extends InnerDelete implements SqlBuilder { public HardDelete(Delete delete) { super(delete); } public MapedSql toMapedSql() throws InstantiationException, IllegalAccessException { StringBuilder sb = new StringBuilder(); sb.append("DELETE FROM ").append(delete.makeSurePojoMeta().getTableName()); return this.toMapedSql(sb, new MapedSql()); } } class SoftDelete extends InnerDelete implements SqlBuilder { public SoftDelete(Delete delete) { super(delete); } public MapedSql toMapedSql() throws InstantiationException, IllegalAccessException { StringBuilder sb = new StringBuilder(); PojoMeta pojoMeta = delete.makeSurePojoMeta(); SoftDeleteMeta sm = pojoMeta.softDelete; sb.append("UPDATE ").append(pojoMeta.getTableName()).append(" SET ").append(sm.columnName).append(" = ? "); MapedSql ms = new MapedSql(); ms.addParam(sm.inValidValue); return this.toMapedSql(sb, ms); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/Insert.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import org.yx.base.ItemJoiner; import org.yx.base.sumk.map.ListMap; import org.yx.common.util.SeqUtil; import org.yx.db.event.InsertEvent; import org.yx.db.visit.SumkDbVisitor; import org.yx.exception.SumkException; public class Insert extends ModifySqlBuilder { protected List src = new ArrayList<>(); public Insert(SumkDbVisitor visitor) { super(visitor); } /** * 分表的情况下,设置分区名。这个方法只能调用一次 * * @param sub 分区名 * @return 当前对象 */ public Insert partition(String sub) { sub(sub); return this; } /** * @param pojo 要插入的对象,该对象不会被DB包修改。如果是Map类型,要设置tableClass
* 但如果对象中主键为null。而且表只有一个主键,并且是Long类型的话,会自动生成一个id,并且赋值到pojo中
* @return 当前对象 */ public Insert insert(Object pojo) { this.src.add(pojo); this._addIn(pojo); return this; } public Insert tableClass(Class tableClass) { this.tableClass = tableClass; return this; } @Override public MapedSql toMapedSql() throws Exception { this.checkIn(); return batchInsert(); } protected MapedSql batchInsert() throws Exception { PojoMeta pojoMeta = this.pojoMeta; MapedSql ms = new MapedSql(); ItemJoiner columns = ItemJoiner.create(",", " ( ", " ) "); ItemJoiner placeholder = ItemJoiner.create(",", " ( ", " ) "); List fms = pojoMeta.fieldMetas(); int recodeSize = in.size(); boolean softDeleteAndNotProvided = pojoMeta.isSoftDelete() && !pojoMeta.softDelete.fieldProvided; for (ColumnMeta fm : fms) { String name = fm.dbColumn; columns.item().append(name); placeholder.item().append('?'); } if (softDeleteAndNotProvided) { String columnName = pojoMeta.softDelete.columnName; columns.item().append(columnName); placeholder.item().append('?'); } StringBuilder sql = new StringBuilder(); sql.append("INSERT INTO ").append(pojoMeta.getTableName()).append(columns.toCharSequence(true)) .append(" VALUES "); CharSequence place = placeholder.toCharSequence(true); for (int i = 0; i < recodeSize; i++) { if (i > 0) { sql.append(','); } sql.append(place); } ms.sql = sql.toString(); List> cacheList = new ArrayList<>(recodeSize); for (int i = 0; i < recodeSize; i++) { Map pojoMap = this.in.get(i); this.checkMap(pojoMap, pojoMeta); Map map = new ListMap<>(pojoMap.size()); this.fillSpecialColumns(pojoMap, src.get(i)); for (ColumnMeta fm : fms) { String key = fm.getFieldName(); Object value = pojoMap.get(key); ms.addParam(value); map.put(key, value); } if (map.isEmpty()) { throw new SumkException(829341, "存在无效的待插入记录"); } cacheList.add(map); if (softDeleteAndNotProvided) { ms.addParam(pojoMeta.softDelete.validValue); } } ms.event = new InsertEvent(pojoMeta.getTableName(), flag, cacheList); return ms; } protected void fillSpecialColumns(Map pojoMap, Object srcObject) throws Exception { List idColumns = pojoMeta.getDatabaseIds(); if (idColumns.size() != 1) { return; } ColumnMeta idColumn = idColumns.get(0); if (idColumn.field.getType() != Long.class) { return; } Long autoId = (Long) idColumn.value(pojoMap); if (autoId == null) { autoId = SeqUtil.next("seq_".concat(pojoMeta.getTableName())); idColumn.setValue(srcObject, autoId); idColumn.setValue(pojoMap, autoId); } List createTimes = pojoMeta.createColumns(); if (createTimes.size() > 0) { Date now = new Date(SeqUtil.getTimeMillis(autoId) / 1000 * 1000); for (ColumnMeta cTime : createTimes) { cTime.setValue(srcObject, now); Object time2 = cTime.value(srcObject); cTime.setValue(pojoMap, time2); } } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/InsertResult.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.List; public class InsertResult { private int effectCount; private List autoGeneratedKeys; public InsertResult(int effectCount, List autoGeneratedKeys) { this.effectCount = effectCount; this.autoGeneratedKeys = autoGeneratedKeys; } public int getEffectCount() { return effectCount; } public List getAutoGeneratedKeys() { return autoGeneratedKeys; } public long getAutoGeneratedKey() { if (autoGeneratedKeys == null || autoGeneratedKeys.isEmpty()) { return -1; } Long row = autoGeneratedKeys.get(0); return row == null ? -1 : row.longValue(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/MapedSql.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.yx.base.ItemJoiner; import org.yx.db.event.DBEvent; public class MapedSql { public MapedSql() { this.paramters = new ArrayList<>(); } public MapedSql(String sql, List paramters) { this.sql = sql; this.paramters = Objects.requireNonNull(paramters); } String sql; private List paramters; DBEvent event; public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public List getParamters() { return paramters; } public void addParam(Object p) { this.paramters.add(p); } public void addParams(List ps) { if (ps == null || ps.isEmpty()) { return; } this.paramters.addAll(ps); } public DBEvent getEvent() { return event; } public void setEvent(DBEvent event) { this.event = event; } @Override public String toString() { return sql + " -- " + paramters; } public static MapedSql merge(List mapeds, ItemJoiner joiner) { if (mapeds == null || mapeds.isEmpty()) { return null; } List params = new ArrayList<>(); for (MapedSql maped : mapeds) { joiner.item().append(maped.sql); params.addAll(maped.paramters); } return new MapedSql(joiner.toCharSequence(true).toString(), params); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/MapedSqlBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.Map; import java.util.function.Function; import org.yx.db.sql.token.MapValueHandler; import org.yx.db.sql.token.MapedSqlTokenParser; import org.yx.db.sql.token.ReplaceTokenHandler; import org.yx.db.sql.token.StringTokenParser; import org.yx.db.sql.token.VariableTokenHandler; public class MapedSqlBuilder implements SqlBuilder { private final String _sql; private final Function valueHandler; public MapedSqlBuilder(String sql, Map map) { this._sql = sql; this.valueHandler = new MapValueHandler(map); } public MapedSqlBuilder(String sql, Function valueHandler) { this._sql = sql; this.valueHandler = valueHandler; } @Override public MapedSql toMapedSql() throws Exception { String sql = _sql; if (sql.contains("${")) { sql = new StringTokenParser("${", "}", ReplaceTokenHandler.create(valueHandler)).parse(sql); } return new MapedSqlTokenParser("#{", "}", VariableTokenHandler.create(valueHandler)).parse(sql); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/ModifySqlBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import org.yx.db.visit.SumkDbVisitor; public abstract class ModifySqlBuilder extends AbstractSqlBuilder { public ModifySqlBuilder(SumkDbVisitor visitor) { super(visitor); } public int execute() { this.makeSurePojoMeta().getCounter().incrModifyCount(); return this.accept(visitor); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/Operation.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; public enum Operation { BIG(" > "), BIG_EQUAL(" >= "), LESS(" < "), LESS_EQUAL(" <= "), LIKE(" LIKE "), NOT_LIKE(" NOT LIKE "), NOT(" <> "), IN(" IN "), NOT_IN(" NOT IN "), EQUAL(" = "); final String op; private Operation(String op) { this.op = op; } public String op() { return op; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/PojoMeta.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.lang.reflect.Array; import java.lang.reflect.Type; import java.sql.Time; import java.time.LocalTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.function.IntFunction; import org.yx.annotation.db.AutoCreateTime; import org.yx.annotation.doc.Comment; import org.yx.annotation.doc.NotNull; import org.yx.base.date.TimeUtil; import org.yx.base.sumk.map.ListMap; import org.yx.conf.AppInfo; import org.yx.db.enums.CacheType; import org.yx.db.spec.TableSpec; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.redis.RedisPool; import org.yx.util.CollectionUtil; import org.yx.util.Loader; import org.yx.util.StringUtil; public final class PojoMeta implements Cloneable { public static final String WILDCHAR = "#"; private static final char KEY_SPLIT = ':'; /** * 数据库表所对应的java pojo类 */ final Class pojoClz; final Type pojoArrayClz; private final CacheType cacheType; private final Map javaNameDict; @NotNull private final List fieldMetas; @NotNull final List databaseIds; @NotNull final List cacheIDs; @NotNull private final List createColumns; private VisitCounter counter; private int ttlSec; private String pre; private String tableName; final SoftDeleteMeta softDelete; public PojoMeta(TableSpec table, List fieldMetas, Class pojoClz) { CacheType tableCacheType = Objects.requireNonNull(table.cacheType()); this.fieldMetas = CollectionUtil.unmodifyList(fieldMetas); this.pojoClz = pojoClz; List rids = new ArrayList<>(); List pids = new ArrayList<>(); List ctimes = new ArrayList<>(); for (ColumnMeta m : this.fieldMetas) { if (tableCacheType != CacheType.NOCACHE && m.isCacheID()) { rids.add(m); } if (m.isDBID()) { pids.add(m); } if (m.field.isAnnotationPresent(AutoCreateTime.class)) { if (TimeUtil.isGenericDate(m.field.getType()) && !timeOnly(m.field.getType())) { ctimes.add(m); } else { Logs.db().warn("{}.{}的类型{}不是@CreateTime支持的类型", pojoClz.getSimpleName(), m.field.getName(), m.field.getType()); } } } this.databaseIds = CollectionUtil.unmodifyList(pids); this.cacheIDs = pids.equals(rids) ? this.databaseIds : CollectionUtil.unmodifyList(rids); this.cacheType = this.cacheIDs.isEmpty() ? CacheType.NOCACHE : tableCacheType; this.createColumns = CollectionUtil.unmodifyList(ctimes); this.softDelete = DBSettings.softDeleteParser().parse(this.pojoClz, this.fieldMetas); this.parseTable(table); this.pojoArrayClz = Array.newInstance(this.pojoClz, 0).getClass(); HashMap dict = new HashMap<>(); for (ColumnMeta cm : this.fieldMetas) { dict.put(cm.getFieldName().toLowerCase(), cm); } this.javaNameDict = dict; } public Type pojoArrayClz() { return this.pojoArrayClz; } public List fieldMetas() { return this.fieldMetas; } public ColumnMeta getByColumnDBName(String columnDBName) { for (ColumnMeta cm : this.fieldMetas) { if (cm.dbColumn.equalsIgnoreCase(columnDBName)) { return cm; } } return null; } public ColumnMeta getByFieldName(String fieldName) { return this.javaNameDict.get(fieldName.toLowerCase()); } public boolean isNoCache() { return cacheType == CacheType.NOCACHE || RedisPool.defaultRedis() == null; } public CacheType cacheType() { return cacheType; } public boolean isPrimeKeySameWithCache() { return databaseIds == cacheIDs; } public List getDatabaseIds() { return databaseIds; } public boolean isSoftDelete() { return this.softDelete != null; } public VisitCounter getCounter() { return counter; } /** * 重置计数器 * * @return 旧计数器 */ public VisitCounter resetCounter() { VisitCounter old = this.counter; int maxHit = AppInfo.getInt("sumk.db.table.cache.maxHit." + this.tableName, old.getInterval()); IntFunction factory = DBSettings.visitCounterFactory(); VisitCounter c = factory != null ? factory.apply(maxHit) : new EstimateVisitCounter(maxHit); this.counter = Objects.requireNonNull(c); return old; } public int getTtlSec() { return ttlSec; } private void parseTable(TableSpec table) { int ttl = table.duration(); if (ttl > 0) { this.ttlSec = ttl; } else if (ttl == 0) { this.ttlSec = AppInfo.getInt("sumk.cache.ttl", 3600); } else { this.ttlSec = -1; } this.tableName = StringUtil.isEmpty(table.value()) ? DBNameResolvers.getTableNameResolver().apply(this.pojoClz.getSimpleName()) : table.value().replace("?", WILDCHAR); String _pre = table.preInCache(); if (StringUtil.isEmpty(_pre)) { _pre = DBNameResolvers.getCachePrefixResolver().apply(this.tableName); } this.pre = _pre.replace("?", WILDCHAR); this.counter = new EstimateVisitCounter(table.maxHit()); this.resetCounter(); } public String getTableName() { return this.tableName; } public String getPre() { return this.pre; } public boolean isOnlyCacheID(Object condition) throws IllegalArgumentException, IllegalAccessException { if (this.pojoClz.isInstance(condition)) { for (ColumnMeta m : this.fieldMetas) { Object v = m.value(condition); if (m.isCacheID() == (v == null)) { return false; } } return true; } if (condition instanceof Map) { @SuppressWarnings("unchecked") Map map = (Map) condition; if (map.size() != this.cacheIDs.size()) { return false; } for (Entry entry : map.entrySet()) { if (entry.getValue() == null) { continue; } String key = entry.getKey(); ColumnMeta cm = this.getByFieldName(key); if (cm == null || !cm.isCacheID()) { return false; } } return true; } return false; } public List getCacheIDs() { return this.cacheIDs; } public Object buildFromDBColumn(Map map) throws Exception { if (map == null) { return null; } Object ret = Loader.newInstance(this.pojoClz); for (Entry en : map.entrySet()) { String key = en.getKey(); ColumnMeta m = this.getByColumnDBName(key); if (m == null) { Logs.db().warn("{}数据库字段{}找不到对应的属性", this.tableName, key); continue; } m.setValue(ret, en.getValue()); } return ret; } @SuppressWarnings("unchecked") public Map populate(Object source, boolean keepNull) throws InstantiationException, IllegalAccessException { if (source instanceof Map) { return (Map) source; } if (!this.pojoClz.isInstance(source)) { throw new SumkException(548092345, source.getClass().getName() + " is not instance of " + this.pojoClz.getName()); } Map map = new ListMap<>(this.fieldMetas.size()); for (ColumnMeta m : this.fieldMetas) { Object v = m.value(source); if (!keepNull && v == null) { continue; } String name = m.getFieldName(); map.put(name, v); } return map; } @SuppressWarnings("unchecked") public Map populateByDbColumn(Object source, boolean withnull) throws InstantiationException, IllegalAccessException { if (source instanceof Map) { return (Map) source; } if (!this.pojoClz.isInstance(source)) { throw new SumkException(548092346, source.getClass().getName() + " is not instance of " + this.pojoClz.getName()); } Map map = new HashMap<>(); for (ColumnMeta m : this.fieldMetas) { Object v = m.value(source); if (!withnull && v == null) { continue; } String name = m.dbColumn; map.put(name, v); } return map; } public String getCacheID(Object source, boolean exceptionIfHasNull) throws Exception { return joinColumns(source, exceptionIfHasNull, this.cacheIDs); } public String getCacheIDWithNULL(Map map) throws Exception { StringBuilder key = new StringBuilder(); for (ColumnMeta m : this.cacheIDs) { Object v = map.get(m.getFieldName()); if (key.length() > 0) { key.append(KEY_SPLIT); } key.append(v); } return key.toString(); } private String joinColumnsFromMap(Map map, boolean exceptionIfHasNull, List cols) { StringBuilder key = new StringBuilder(); for (ColumnMeta m : cols) { Object v = map.get(m.getFieldName()); if (v == null) { if (exceptionIfHasNull) { throw new SumkException(1232142356, this.pojoClz.getName() + ": redis key [" + m.getFieldName() + "] cannot be null"); } return null; } if (key.length() > 0) { key.append(KEY_SPLIT); } key.append(v); } return key.toString(); } @SuppressWarnings("unchecked") public String joinColumns(Object source, boolean exceptionIfHasNull, List cols) throws Exception { if (source instanceof Map) { return this.joinColumnsFromMap((Map) source, exceptionIfHasNull, cols); } StringBuilder key = new StringBuilder(); for (ColumnMeta m : cols) { Object v = m.value(source); if (v == null) { if (exceptionIfHasNull) { throw new SumkException(1232142356, this.pojoClz.getName() + ": value of " + m.getFieldName() + " cannot be null"); } return null; } if (key.length() > 0) { key.append(KEY_SPLIT); } key.append(v); } return key.toString(); } public PojoMeta subPojoMeta(String sub) { if (!this.tableName.contains(WILDCHAR)) { return this; } PojoMeta clone; try { clone = (PojoMeta) this.clone(); } catch (CloneNotSupportedException e) { throw new SumkException(3456346, e.getMessage()); } clone.tableName = subTableName(sub); clone.pre = this.pre.replace(WILDCHAR, sub.toLowerCase()); return clone; } String subTableName(String sub) { return this.tableName.replace(WILDCHAR, sub); } public List createColumns() { return createColumns; } private static boolean timeOnly(Class type) { return type == Time.class || type == LocalTime.class; } public Class pojoClz() { return this.pojoClz; } public String getComment() { Comment c = this.pojoClz.getAnnotation(Comment.class); return c == null ? "" : c.value(); } public SoftDeleteMeta getSoftDelete() { return softDelete; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/PojoMetaHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.yx.db.spec.TableSpec; import org.yx.exception.SumkException; public class PojoMetaHolder { private static final ConcurrentMap, PojoMeta> pojoMetas = new ConcurrentHashMap<>(); private static final ConcurrentMap tableMetas = new ConcurrentHashMap<>(); public static PojoMeta getTableMeta(String table) { return tableMetas.get(table); } public static List allPojoMeta() { return new ArrayList<>(pojoMetas.values()); } public static PojoMeta getPojoMeta(PojoMeta pm, String sub) { PojoMeta temp = tableMetas.get(pm.subTableName(sub)); if (temp != null) { return temp; } pm = pm.subPojoMeta(sub); temp = tableMetas.putIfAbsent(pm.getTableName(), pm); return temp != null ? temp : pm; } public static PojoMeta getPojoMeta(Class clz, String sub) { PojoMeta pm = getPojoMeta(clz); if (pm == null) { return null; } if (sub == null || sub.isEmpty()) { return pm; } PojoMeta temp = tableMetas.get(pm.subTableName(sub)); if (temp != null) { return temp; } pm = pm.subPojoMeta(sub); temp = tableMetas.putIfAbsent(pm.getTableName(), pm); return temp != null ? temp : pm; } public static PojoMeta getPojoMeta(Class clz) { if (clz == null || clz.isInterface() || clz == Object.class) { return null; } Class tmp = clz; while (tmp != Object.class) { PojoMeta m = pojoMetas.get(tmp); if (m != null) { return m; } tmp = tmp.getSuperclass(); } return null; } public static void register(Class pojoClz, TableSpec table, List columns) { PojoMeta tm = new PojoMeta(table, columns, pojoClz); if (tm.databaseIds.isEmpty()) { throw new SumkException(56456456, pojoClz.getName() + " has no database primary key"); } pojoMetas.put(pojoClz, tm); tableMetas.put(tm.getTableName(), tm); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/RawSqlBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class RawSqlBuilder implements SqlBuilder { private String _sql; private List _params; @SuppressWarnings("unchecked") public RawSqlBuilder(String sql, Object[] params) { this._sql = sql; if (params == null || params.length == 0) { this._params = new ArrayList<>(); } else if (params.length == 1 && params[0] instanceof List) { this._params = (List) params[0]; } else { this._params = Arrays.asList(params); } } @Override public MapedSql toMapedSql() throws Exception { MapedSql ms = new MapedSql(); ms.setSql(_sql); ms.addParams(_params); return ms; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/Select.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import static org.yx.db.sql.Operation.BIG; import static org.yx.db.sql.Operation.BIG_EQUAL; import static org.yx.db.sql.Operation.IN; import static org.yx.db.sql.Operation.LESS; import static org.yx.db.sql.Operation.LESS_EQUAL; import static org.yx.db.sql.Operation.LIKE; import static org.yx.db.sql.Operation.NOT; import static org.yx.db.sql.Operation.NOT_IN; import static org.yx.db.sql.Operation.NOT_LIKE; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import org.yx.base.sumk.map.ListMap; import org.yx.common.util.kit.Asserts; import org.yx.db.enums.CacheType; import org.yx.db.event.DBEventPublisher; import org.yx.db.event.QueryEvent; import org.yx.db.kit.DBKits; import org.yx.db.visit.Exchange; import org.yx.db.visit.PojoResultHandler; import org.yx.db.visit.ResultHandler; import org.yx.db.visit.SumkDbVisitor; import org.yx.exception.SumkException; import org.yx.util.CollectionUtil; /** * 比较跟整个addEqual是add关系。同一种比较类型,比如less,它的一个key只能设置一次,后设置的会覆盖前面设置的
* 比较中用到的key,都是java中的key,大小写敏感. */ public class Select extends SelectBuilder { public Select(SumkDbVisitor>> visitor) { super(visitor); } /** * @param onOff 如果为true,会验证map参数中,是否存在无效的key,预防开发人员将key写错。默认为true * @return 当前对象 */ public Select failIfPropertyNotMapped(boolean onOff) { this.setOnOff(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, onOff); return this; } /** * 物理分表的情况下,设置分区名。这个方法只能调用一次 * * @param sub 分区名 * @return 当前对象 */ public Select partition(String sub) { sub(sub); return this; } /** * 允许不设置where条件 * * @param empty true表示允许where条件为空 * @return 当前对象 */ public Select allowEmptyWhere(boolean empty) { this.setOnOff(DBFlag.SELECT_ALLOW_EMPTY_WHERE, empty); return this; } protected ResultHandler resultHandler; public Select resultHandler(ResultHandler resultHandler) { this.resultHandler = Objects.requireNonNull(resultHandler); return this; } public Select compareAllowNull(boolean onOff) { this.setOnOff(DBFlag.SELECT_COMPARE_ALLOW_NULL, onOff); return this; } public Select compareIgnoreNull(boolean onOff) { this.setOnOff(DBFlag.SELECT_COMPARE_IGNORE_NULL, onOff); return this; } protected Select andCompares(Operation op, Object pojo) { Map map = this.populate(pojo, false); if (CollectionUtil.isEmpty(map)) { return this; } for (Entry en : map.entrySet()) { this.and(op, en.getKey(), en.getValue()); } return this; } private Select and(Operation op, String key, Object value) { this._compare.and(key, op, value); return this; } /** * 设置大于,一个key只能设置一次,后设置的会覆盖前面设置的。
* * @param key java字段的名称 * @param value 值 * @return 当前对象 */ public Select bigThan(String key, Object value) { return and(BIG, key, value); } public Select bigOrEqual(String key, Object value) { return and(BIG_EQUAL, key, value); } public Select lessThan(String key, Object value) { return and(LESS, key, value); } public Select lessOrEqual(String key, Object value) { return and(LESS_EQUAL, key, value); } /** * like操作,%号要自己添加 * * @param key 字段名 * @param value 值,不会自动添加% * @return 当前对象 */ public Select like(String key, Object value) { return and(LIKE, key, value); } public Select notLike(String key, Object value) { return and(NOT_LIKE, key, value); } /** * 增加一些特别的表达式,主要是为了扩展对OR操作符的支持 * * @param op 一般使用GroupOR.create()来创建一个or表达式 * @return 当前对象 */ public Select and(CompareOperation op) { this._compare.and(op); return this; } /** * 不等于操作 * * @param key 字段名 * @param value 值,不会自动添加% * @return 当前对象 */ public Select not(String key, Object value) { return and(NOT, key, value); } /** * sql中的in查询 * * @param key 字段名 * @param values 值列表,不能为空 * @return 当前对象 */ public Select in(String key, Collection values) { return and(IN, key, CollectionUtil.unmodifyList(values)); } public Select notIn(String key, Collection values) { return and(NOT_IN, key, CollectionUtil.unmodifyList(values)); } public Select bigThan(Object pojo) { return andCompares(BIG, pojo); } /** * 大于等于 * * @param pojo 对pojo中所有的kv做大于等于操作 * @return 当前对象 */ public Select bigOrEqual(Object pojo) { return andCompares(BIG_EQUAL, pojo); } public Select lessThan(Object pojo) { return andCompares(LESS, pojo); } /** * 小于或等于 * * @param pojo 对pojo中所有的kv做小于等于操作 * @return 当前对象 */ public Select lessOrEqual(Object pojo) { return andCompares(LESS_EQUAL, pojo); } public Select like(Object pojo) { return andCompares(LIKE, pojo); } public Select not(Object pojo) { return andCompares(NOT, pojo); } /** * 根据字段名和判断条件移除所有符合条件的比较。只移除第一级的条件 * * @param key java字段名,可以为null。null表示所有的字段都要移除 * @param op 比较条件,可以为null。null表示所有的条件都要移除 * @return 当前对象 */ public Select removeCompares(String key, Operation op) { this._compare.removeCompares(key, op); return this; } /** * 升序排列。asc和desc的调用顺序决定了在sql中出现的顺序。 此方法可以调用多次 * * @param field 升序字段 * @return 当前对象 */ public Select orderByAsc(String field) { return this.addOrderBy(field, false); } protected Select addOrderBy(String name, boolean desc) { if (this.orderby == null) { this.orderby = new ArrayList<>(2); } this.orderby.add(new Order(name, desc)); return this; } /** * 增加降序排列 * * @param field 降序字段 * @return 当前对象 */ public Select orderByDesc(String field) { return this.addOrderBy(field, true); } /** * 设置查询的便宜量,从0开始。 * * @param offset from的位置 * @return 当前对象 */ public Select offset(int offset) { if (this.isOn(DBFlag.SELECT_IGNORE_MAX_OFFSET)) { if (offset < 0 || offset > DBSettings.MaxOffset()) { throw new SumkException(235345347, "offset需要在0-" + DBSettings.MaxOffset() + "之间,通过ignoreMaxOffset(true)可以取消这个限制"); } } this.offset = offset; return this; } /** * * @param limit 返回的最大条数。 * @return 当前对象 */ public Select limit(int limit) { if (this.isOn(DBFlag.SELECT_IGNORE_MAX_LIMIT)) { if (limit <= 0 || limit > DBSettings.MaxLimit()) { throw new SumkException(235345346, "limit需要在1-" + DBSettings.MaxLimit() + "之间,通过ignoreMaxLimit(true)可以取消这个限制"); } } this.limit = limit; return this; } /** * * @param columns 设置查询放回的列,列名是java中的字段名。如果不设,将返回所有的字段 * @return 当前对象 */ public Select selectColumns(String... columns) { if (columns == null || columns.length == 0) { this.selectColumns = null; return this; } this.selectColumns = CollectionUtil.unmodifyList(columns); return this; } /** * 如果为false,就不会从缓存中加载数据 * * @param fromCache 默认为true。sumk.sql.fromCache=false可以将全局参数设为false * @return 当前对象 */ public Select fromCache(boolean fromCache) { this.setOnOff(DBFlag.SELECT_FROM_CACHE, fromCache); return this; } /** * 如果为false,查出的结果将不会用于更新缓存 * * @param toCache 该参数设为true的实际意义不大 * @return 当前对象 */ public Select toCache(boolean toCache) { this.setOnOff(DBFlag.SELECT_TO_CACHE, toCache); return this; } public Select ignoreMaxLimit(boolean on) { this.setOnOff(DBFlag.SELECT_IGNORE_MAX_LIMIT, on); return this; } public Select ignoreMaxOffset(boolean on) { this.setOnOff(DBFlag.SELECT_IGNORE_MAX_OFFSET, on); return this; } /** * 设置相等的条件。本方法可以被多次执行。 src中的各个条件是and关系。不同src之间是or关系
* 注意:如果pojo是map类型,那么它的null值是有效条件 * * @param src map或pojo类型。 * @return 当前对象 */ public Select addEqual(Object src) { this._addIn(src); return this; } /** * 各个addEqual之间的条件是OR,如果要组装AND条件,请用addEqual(Object src) * * @param field 字段名 * @param value 要查询的条件的值 * @return 当前对象 */ public Select addEqual(String field, Object value) { this._addInByMap(Collections.singletonMap(field, value)); return this; } /** * 通过数据库主键列表查询主键,单主键ids就只有一个,多主键就传入多个 注意:调用本方法之前,要确保调用过tableClass()方法 * * @param ids id列表,顺序跟pojo中定义的一致(按order顺序或书写顺序) * @return 注意:调用本方法之前,要确保调用过tableClass()方法 */ public Select byDatabaseId(Object... ids) { return byId(true, ids); } /** * 通过redis主键列表查询主键,单主键ids就只有一个,多主键就传入多个
* 所有id属于同一条记录,如果要使用单主键的in查询,请用本类的in方法
* 注意:调用本方法之前,要确保调用过tableClass()方法 * * @param ids id列表,顺序跟pojo中定义的一致(按order顺序或书写顺序) * @return 当前对象 * */ public Select byCacheId(Object... ids) { return byId(false, ids); } protected Select byId(boolean databaseId, Object... ids) { if (ids == null || ids.length == 0) { return this; } makeSurePojoMeta(); List cms = databaseId ? this.pojoMeta.getDatabaseIds() : this.pojoMeta.getCacheIDs(); Asserts.requireTrue(cms != null && cms.size() == ids.length, pojoMeta.getTableName() + "没有设置主键,或者主键个数跟参数个数不一致"); Map map = new ListMap<>(cms.size()); for (int i = 0; i < ids.length; i++) { map.put(cms.get(i).getFieldName(), ids[i]); } _addInByMap(map); return this; } public Select tableClass(Class tableClass) { this.tableClass = tableClass; return this; } protected ResultHandler resultHandler() { return this.resultHandler == null ? PojoResultHandler.handler : this.resultHandler; } private boolean isCompareOnlyCacheId() { if (_compare.size() != 1) { return false; } CompareOperation c0 = _compare.get(0); if (!(c0 instanceof ColumnOperation)) { return false; } ColumnOperation cp = (ColumnOperation) c0; return cp.getType() == IN && cp.getName().equals(this.pojoMeta.cacheIDs.get(0).field.getName()); } protected boolean canUseInCache() { return this.pojoMeta.cacheIDs.size() == 1 && CollectionUtil.isEmpty(this.in) && this.pojoMeta.cacheType() == CacheType.SINGLE && isCompareOnlyCacheId(); } protected List queryFromCache(Exchange exchange) throws Exception { if (CollectionUtil.isEmpty(this.in) && this._compare.isEmpty()) { return null; } boolean fromCache = this.isOn(DBFlag.SELECT_FROM_CACHE); if (!(fromCache && this.orderby == null && this.offset == 0 && !pojoMeta.isNoCache())) { return null; } String singleKeyName = this.canUseInCache() ? this.pojoMeta.cacheIDs.get(0).field.getName() : null; if (singleKeyName != null) { ColumnOperation cp = (ColumnOperation) _compare.get(0); List vs = (List) cp.getValue(); List> newIN = new ArrayList<>(vs.size()); for (Object v : vs) { newIN.add(Collections.singletonMap(singleKeyName, v)); } exchange.setLeftIn(newIN); } else if (_compare.isEmpty()) { exchange.setLeftIn(this.in); } else { return null; } List list = new ArrayList<>(); exchange.findFromCache(pojoMeta); if (exchange.getData() != null && exchange.getData().size() > 0) { List tmp = this.resultHandler().parseFromJson(pojoMeta, exchange.getData(), CollectionUtil.unmodifyList(this.selectColumns)); if (tmp != null && tmp.size() > 0) { list.addAll(tmp); } } List> left = exchange.getLeftIn(); if (left == null) { return list; } if (singleKeyName != null) { List vs = new ArrayList<>(left.size()); for (Map m : left) { vs.add(m.get(singleKeyName)); } this._compare = GroupAND.create().and(singleKeyName, IN, vs); } else { this.in = left; } return list; } protected final void validSelectColumns() { if (this.selectColumns == null || this.selectColumns.isEmpty()) { this.selectColumns = null; return; } List list = new ArrayList<>(this.selectColumns.size()); for (String filedName : selectColumns) { ColumnMeta cm = pojoMeta.getByFieldName(filedName); if (cm == null) { this.failIfNotAllowPropertyMiss(filedName); continue; } list.add(cm.getFieldName()); } this.selectColumns = list.isEmpty() ? null : list; } public List queryList() { final List> origin = this.in = CollectionUtil.unmodifyList(this.in); final GroupAND orginCompare = this._compare.unmodifyFirstLevel(); try { makeSurePojoMeta().getCounter().incrQueryCount(); this.validSelectColumns(); Exchange exchange = new Exchange(); List list = this.queryFromCache(exchange); if (list != null && CollectionUtil.isEmpty(exchange.getLeftIn())) { return list; } boolean canUseCache = true; if (list == null) { list = Collections.emptyList(); canUseCache = false; } List dbData = this.resultHandler().parse(pojoMeta, this.accept(visitor)); if (dbData == null || dbData.isEmpty()) { return list; } list = merge(list, dbData); List> eventIn = canUseCache ? exchange.getLeftIn() : this.in; if (this.isOn(DBFlag.SELECT_TO_CACHE) && selectColumns == null && this.offset <= 0 && (canUseCache || this._compare.isEmpty()) && (limit <= 0 || limit >= DBSettings.MaxLimit()) && CollectionUtil.isNotEmpty(eventIn) && dbData.size() < DBSettings.maxQueryCacheSize()) { QueryEvent event = new QueryEvent(this.pojoMeta.getTableName()); event.setIn(eventIn); event.setResult(dbData); DBEventPublisher.publishQuery(event); } if (this.limit > 0 && list.size() > this.limit) { return list.subList(0, this.limit); } return list; } catch (Exception e) { throw SumkException.wrap(e); } finally { this.in = origin; this._compare = orginCompare; } } protected List merge(List cacheList, List dbList) throws Exception { if (cacheList.isEmpty()) { return dbList; } List ret = new ArrayList<>(cacheList.size() + dbList.size()); ret.addAll(dbList); Set keys = new HashSet<>(); PojoMeta pm = this.pojoMeta; List columns = pm.databaseIds; for (T t : dbList) { String key = pm.joinColumns(t, false, columns); if (key != null) { keys.add(key); } } for (T t : cacheList) { String key = pm.joinColumns(t, false, columns); if (key == null || keys.add(key)) { ret.add(t); } } return ret; } public T queryOne() { return DBKits.queryOne(this.queryList()); } /** * 根据select的条件,查询符合条件的记录数。其中offset、limit、order by属性被过滤掉
* 这个方法可以在select执行前调用,也可以在select执行后调用 * * @return 符合条件的数据库记录数 */ public long count() { return new Count(this).execute(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/SelectBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.StringJoiner; import org.yx.base.ItemJoiner; import org.yx.db.visit.SumkDbVisitor; import org.yx.exception.SumkException; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class SelectBuilder extends AbstractSqlBuilder>> { protected List selectColumns; protected GroupAND _compare = GroupAND.create(); protected List orderby; protected int offset; protected int limit; public SelectBuilder(SumkDbVisitor>> visitor) { super(visitor); this.limit = DBSettings.MaxLimit(); } /** * 这个方法用于两个地方,一个是框架内部调用,一旦外部调用有可能影响最终结果 另一个是开发调试的时候,调用本方法查看最终的sql,这时候它不需要放在事务中 * * @throws Exception 异常信息 */ @Override public MapedSql toMapedSql() throws Exception { makeSurePojoMeta(); List paramters = new ArrayList<>(10); StringBuilder sql = new StringBuilder(32); sql.append("SELECT ").append(this.buildField()).append(" FROM ").append(this.pojoMeta.getTableName()); CharSequence where = this.buildWhere(paramters); if (StringUtil.isNotEmpty(where)) { sql.append(" WHERE ").append(where); } CharSequence order = buildOrder(); if (StringUtil.isNotEmpty(order)) { sql.append(" ORDER BY ").append(order); } buildLimitAndOffset(sql); return new MapedSql(sql.toString(), paramters); } /** * 组装分页,也就是offset和limit * * @param sql 已组装出来的sql */ protected void buildLimitAndOffset(StringBuilder sql) { if (limit > 0) { sql.append(" LIMIT ").append(this.limit); } if (offset > 0) { sql.append(" OFFSET ").append(this.offset); } } protected CharSequence buildOrder() { if (CollectionUtil.isEmpty(this.orderby)) { return null; } StringBuilder sb = new StringBuilder(); for (Order order : this.orderby) { if (sb.length() > 0) { sb.append(','); } sb.append(order.toString(this.pojoMeta)); } return sb; } protected CharSequence buildField() { PojoMeta pojoMeta = this.pojoMeta; StringJoiner sj = new StringJoiner(","); if (this.selectColumns != null && this.selectColumns.size() > 0) { for (String filedName : this.selectColumns) { ColumnMeta cm = pojoMeta.getByFieldName(filedName); if (cm == null) { this.failIfNotAllowPropertyMiss(filedName); continue; } sj.add(cm.dbColumn); } return sj.toString(); } for (ColumnMeta cm : pojoMeta.fieldMetas()) { sj.add(cm.dbColumn); } return sj.toString(); } protected CharSequence buildWhere(List paramters) { ItemJoiner joiner = new ItemJoiner(" AND ", null, null); joiner.appendNotEmptyItem(buildEquals(paramters)).appendNotEmptyItem(buildCompare(paramters)); if (joiner.isEmpty() && !this.isOn(DBFlag.SELECT_ALLOW_EMPTY_WHERE)) { throw new SumkException(63254325, "empty where"); } return joiner.appendNotEmptyItem(buildValid(paramters)).toCharSequence(); } private CharSequence buildValid(List paramters) { SoftDeleteMeta softDelete = this.pojoMeta.softDelete; if (softDelete == null) { return null; } StringBuilder sb = new StringBuilder(); if (softDelete.equalValid) { sb.append(softDelete.columnName).append(" = ?"); paramters.add(softDelete.validValue); } else { sb.append(softDelete.columnName).append(" <> ?"); paramters.add(softDelete.inValidValue); } return sb; } private CharSequence buildCompare(List paramters) { return this._compare.buildSql(this, paramters); } private CharSequence buildEquals(List paramters) { if (this.in == null || this.in.isEmpty()) { return null; } ItemJoiner joiner = ItemJoiner.create(" OR ", " ( ", " ) "); List> list = this.in; for (Map map : list) { CharSequence sub = this.parseEqual(map, paramters); if (sub == null) { continue; } joiner.item().append(sub); } return joiner.toCharSequence(); } private CharSequence parseEqual(Map src, List paramters) { if (CollectionUtil.isEmpty(src)) { return null; } ItemJoiner joiner = ItemJoiner.create(" AND ", " ( ", " ) "); for (Entry entry : src.entrySet()) { String filedName = entry.getKey(); Object v = entry.getValue(); ColumnMeta cm = pojoMeta.getByFieldName(filedName); if (cm == null) { if (this.isOn(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED)) { throw new SumkException(7331234, filedName + "这个字段没有在java的pojo类中定义"); } continue; } if (v == null) { joiner.item().append(cm.dbColumn).append(" IS NULL"); continue; } joiner.item().append(cm.dbColumn).append(" = ?"); paramters.add(v); } return joiner.toCharSequence(); } protected static class Order { final String name; final boolean desc; public Order(String name, boolean desc) { this.name = name; this.desc = desc; } public String toString(PojoMeta pm) { ColumnMeta cm = pm.getByFieldName(name); if (cm == null) { throw new SumkException(4532018, "排序字段" + name + "不在" + pm.pojoClz.getName() + "字段中"); } String dbName = cm.dbColumn; if (desc) { return dbName + " desc"; } return dbName; } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/SoftDeleteMeta.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; public final class SoftDeleteMeta { final String columnName; final Object validValue; final Object inValidValue; final boolean fieldProvided; final boolean equalValid; public SoftDeleteMeta(String columnName, Object validValue, Object inValidValue, boolean equal, boolean fieldProvided) { this.columnName = columnName; this.validValue = validValue; this.inValidValue = inValidValue; this.equalValid = equal; this.fieldProvided = fieldProvided; } public String getColumnName() { return columnName; } public Object getValidValue() { return validValue; } public Object getInValidValue() { return inValidValue; } public boolean isEqualValid() { return equalValid; } public boolean isFieldProvided() { return fieldProvided; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/SoftDeleteParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.List; public interface SoftDeleteParser { SoftDeleteMeta parse(Class pojoClz, List fieldMetas); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/SoftDeleteParserImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.List; import org.yx.annotation.db.SoftDelete; import org.yx.db.enums.ValidRecord; import org.yx.exception.SumkException; public class SoftDeleteParserImpl implements SoftDeleteParser { public Object parseValue(Class type, String value) { if (type == String.class) { return value; } if (Integer.class == type || int.class == type) { return Integer.valueOf(value); } if (Byte.class == type || byte.class == type) { return Byte.valueOf(value); } if (Short.class == type || short.class == type) { return Short.valueOf(value); } if (Long.class == type || long.class == type) { return Long.valueOf(value); } if (Double.class == type || double.class == type) { return Double.valueOf(value); } if (Float.class == type || float.class == type) { return Float.valueOf(value); } throw new SumkException(6234267, type.getName() + " is not supported by soft delete"); } @Override public SoftDeleteMeta parse(Class pojoClz, List fieldMetas) { SoftDelete sd = pojoClz.getAnnotation(SoftDelete.class); if (sd == null) { return null; } String name = sd.value(); boolean provided = false; for (ColumnMeta f : fieldMetas) { if (name.equalsIgnoreCase(f.getFieldName())) { provided = true; break; } } if (sd.type() == Boolean.class || sd.type() == boolean.class) { return new SoftDeleteMeta(sd.value(), Boolean.TRUE, Boolean.FALSE, true, provided); } boolean equal = sd.whatIsValid() == ValidRecord.EQUAL_VALID; return new SoftDeleteMeta(sd.value(), parseValue(sd.type(), sd.validValue()), parseValue(sd.type(), sd.inValidValue()), equal, provided); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/SqlBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; public interface SqlBuilder { MapedSql toMapedSql() throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/SqlLog.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import org.yx.db.visit.SumkStatement; public interface SqlLog { void log(SumkStatement state, int totalTime, Throwable ex); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/TableBootWatcher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.yx.annotation.Exclude; import org.yx.bean.ParallelBootWatcher; import org.yx.common.util.S; import org.yx.db.spec.DBSpecs; import org.yx.db.spec.TableSpec; import org.yx.log.Logs; public class TableBootWatcher extends ParallelBootWatcher { public TableBootWatcher() { DBSettings.init(); } @Override protected void handle(Class clz) throws Exception { TableSpec spec = DBSpecs.extractTable(clz); if (spec == null) { return; } List columns = this.extractColumns(clz); PojoMetaHolder.register(clz, spec, columns); } public List extractColumns(Class pojoClz) { Field[] fs = S.bean().getFields(pojoClz); Map map = new LinkedHashMap<>(); for (Field f : fs) { if (f.isAnnotationPresent(Exclude.class)) { continue; } map.putIfAbsent(f.getName(), f); } Collection set = map.values(); List list = new ArrayList<>(set.size()); for (Field f : set) { if (!f.isAccessible()) { f.setAccessible(true); } list.add(new ColumnMeta(f, DBSpecs.extractColumn(pojoClz, f))); } if (list.isEmpty()) { Logs.db().debug("{}'s column is empty", pojoClz.getName()); return Collections.emptyList(); } Collections.sort(list); return list; } @Override public int order() { return 2000; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/Update.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.yx.base.ItemJoiner; import org.yx.base.sumk.map.ListMap; import org.yx.common.util.kit.TypeConverter; import org.yx.db.event.UpdateEvent; import org.yx.db.visit.SumkDbVisitor; import org.yx.exception.SumkException; import org.yx.util.CollectionUtil; public class Update extends ModifySqlBuilder { private Map updateTo; private Map incrMap; /** * @param update 如果为false,则数据库主键不会被更新。默认为false。 * @return 当前对象 */ public Update updateDBID(boolean update) { this.setOnOff(DBFlag.UPDATE_UPDATE_DBID, update); return this; } /** * * @param onOff 如果为true,会验证map参数中,是否存在无效的key,预防开发人员将key写错。默认为true * @return 当前对象 */ public Update failIfPropertyNotMapped(boolean onOff) { this.setOnOff(DBFlag.FAIL_IF_PROPERTY_NOT_MAPPED, onOff); return this; } /** * 设置where条件,如果没有设置该条件。就用pojo的数据库主键或者redis主键 *
    *
  • 本方法可以被多次调用,多次调用之间是OR关系
  • *
  • 注意:如果本表使用了缓存,本参数必须包含所有redis主键
  • *
  • 注意:如果pojo是map类型,那么它的null值是有效条件
  • *
* * @param pojo bean类型或Map.如果是pojo对象,其中的null字段会被忽略掉 * @return 当前对象 */ public Update addWhere(Object pojo) { this._addIn(pojo); return this; } public Update(SumkDbVisitor visitor) { super(visitor); } /** * @param fullUpdate 设置为true的话,整条记录全部更新,包括null字段。默认为false * @return 当前对象 */ public Update fullUpdate(boolean fullUpdate) { this.setOnOff(DBFlag.UPDATE_FULL_UPDATE, fullUpdate); return this; } /** * 分表的情况下,设置分区名。这个方法只能调用一次 * * @param sub 分区名 * @return 当前对象 */ public Update partition(String sub) { sub(sub); return this; } /** * 记录被更新后的最终状态。 *
    *
  • 有可能是部分字段,有可能是全部字段
  • *
  • 有可能只是单条记录变成这样,有可能是多条记录变成这样
  • *
* * @param pojo Pojo或Map类型.如果是Map类型,要设置tableClass。 * 如果本表使用了缓存,并且没有where条件,本参数必须包含所有redis主键 * 如果本字段包含在自增长里面,那它将会被排除掉 * @return 当前对象 */ public Update updateTo(Object pojo) { this.updateTo = this.populate(pojo, false); return this; } public Update tableClass(Class tableClass) { this.tableClass = tableClass; return this; } @Override public MapedSql toMapedSql() throws Exception { if (this.updateTo == null) { this.updateTo = Collections.emptyMap(); } if (this.updateTo.isEmpty() && CollectionUtil.isEmpty(this.incrMap)) { throw new SumkException(3464601, "updateTo is null or empty"); } this.checkMap(this.updateTo, this.pojoMeta); if (CollectionUtil.isEmpty(this.in)) { this.addDBIDs2Where(); } return _toMapedSql(); } protected CharSequence buildSingleEqual(Map oneEqual, MapedSql ms) { if (oneEqual.isEmpty()) { return null; } ItemJoiner andItem = new ItemJoiner(" AND ", " ( ", " ) "); for (Entry en : oneEqual.entrySet()) { ColumnMeta fm = pojoMeta.getByFieldName(en.getKey()); if (fm == null) { this.failIfNotAllowPropertyMiss(en.getKey()); continue; } Object value = en.getValue(); if (value == null) { andItem.item().append(fm.dbColumn).append(" IS NULL"); continue; } andItem.item().append(fm.dbColumn).append(" = ?"); ms.addParam(value); } if (andItem.isEmpty()) { return null; } SoftDeleteMeta softDelete = pojoMeta.softDelete; if (softDelete != null) { if (softDelete.equalValid) { andItem.item().append(softDelete.columnName).append(" = ?"); ms.addParam(softDelete.validValue); } else { andItem.item().append(softDelete.columnName).append(" <> ?"); ms.addParam(softDelete.inValidValue); } } return andItem.toCharSequence(); } protected Map buildSetUpdateField(StringBuilder sb, MapedSql mapedSql) { if (this.incrMap != null) { this.checkMap(incrMap, pojoMeta); } boolean notFirst = false; Map to = new ListMap<>(this.updateTo); Object value; for (ColumnMeta fm : pojoMeta.fieldMetas()) { final String fieldName = fm.getFieldName(); if (this.incrMap != null && (value = this.incrMap.get(fieldName)) != null) { to.remove(fieldName); sb.append(notFirst ? " , " : " SET ").append(fm.dbColumn).append(" = ").append(fm.dbColumn) .append(" + ?"); mapedSql.addParam(value); notFirst = true; continue; } value = this.updateTo.get(fieldName); if (value == null && !this.updateTo.containsKey(fieldName) && !this.isOn(DBFlag.UPDATE_FULL_UPDATE)) { continue; } if (fm.isDBID() && !this.isOn(DBFlag.UPDATE_UPDATE_DBID)) { continue; } mapedSql.addParam(value); sb.append(notFirst ? " , " : " SET ").append(fm.dbColumn).append(" = ?"); notFirst = true; } return to; } protected MapedSql _toMapedSql() throws Exception { MapedSql mapedSql = new MapedSql(); StringBuilder sb = new StringBuilder(64).append("UPDATE ").append(pojoMeta.getTableName()); Map to = this.buildSetUpdateField(sb, mapedSql); sb.append(" WHERE "); ItemJoiner orItem = new ItemJoiner(" OR ", null, null); for (Map oneEqual : this.in) { CharSequence one = this.buildSingleEqual(oneEqual, mapedSql); if (one != null && one.length() > 0) { orItem.item().append(one); } } CharSequence whereStr = orItem.toCharSequence(); if (whereStr == null || whereStr.length() == 0) { throw new SumkException(7345445, "where cannot be null"); } sb.append(whereStr); mapedSql.sql = sb.toString(); mapedSql.event = new UpdateEvent(pojoMeta.getTableName(), flag, to, this.incrMap, this.in); return mapedSql; } protected void addDBIDs2Where() throws Exception { List whereColumns = this.pojoMeta.getDatabaseIds(); Map paramMap = new ListMap<>(whereColumns.size()); for (ColumnMeta fm : whereColumns) { String fieldName = fm.getFieldName(); paramMap.put(fieldName, this.updateTo.get(fieldName)); } this._addInByMap(paramMap); } /** * 增加或减少表中数字类型字段的值 * * @param fieldName 需要增长或减少的字段的名字,不能是redis主键 * @param v 如果是减少,直接用负数就行了 * @return 当前对象 */ public Update incrNum(String fieldName, Number v) { if (v == null) { throw new SumkException(5349238, "cannot incr " + fieldName + "(java) by null"); } PojoMeta pm = makeSurePojoMeta(); ColumnMeta columnMeta = pm.getByFieldName(fieldName); if (columnMeta == null) { throw new SumkException(5912239, "cannot found java field " + fieldName + " in " + pm.pojoClz); } v = TypeConverter.toType(v, columnMeta.field.getType(), true); if (this.incrMap == null) { this.incrMap = new ListMap<>(); } this.incrMap.put(fieldName, v); return this; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/VisitCounter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql; /** * 数据库及cache的访问计数器 */ public interface VisitCounter { public int getInterval(); long getCacheKeyVisits(); long getCacheKeyHits(); long getModifyCount(); long getQueryCount(); void incrCacheHit(int c); void incrModifyCount(); void incrQueryCount(); boolean willVisitCache(int c); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/token/MapValueHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql.token; import java.util.Collections; import java.util.Map; import java.util.function.Function; public class MapValueHandler implements Function { private final Map map; public MapValueHandler(Map param) { this.map = param == null ? Collections.emptyMap() : param; } @Override public Object apply(String key) { return map.get(key); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/token/MapedSqlTokenParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql.token; import java.util.ArrayList; import java.util.List; import org.yx.db.sql.MapedSql; public class MapedSqlTokenParser { private final String openToken; private final String closeToken; private final TokenHandler handler; public MapedSqlTokenParser(String openToken, String closeToken, TokenHandler handler) { this.openToken = openToken; this.closeToken = closeToken; this.handler = handler; } public MapedSql parse(String text) { StringBuilder builder = new StringBuilder(); List paramters = new ArrayList<>(); if (text != null && text.length() > 0) { char[] src = text.toCharArray(); int offset = 0; int start = text.indexOf(openToken, offset); while (start > -1) { if (start > 0 && src[start - 1] == '\\') { builder.append(src, offset, start - offset - 1).append(openToken); offset = start + openToken.length(); } else { int end = text.indexOf(closeToken, start); if (end == -1) { builder.append(src, offset, src.length - offset); offset = src.length; } else { builder.append(src, offset, start - offset); offset = start + openToken.length(); String content = new String(src, offset, end - offset); String value = handler.handleToken(content, paramters); builder.append(value != null ? value : this.openToken + content + this.closeToken); offset = end + closeToken.length(); } } start = text.indexOf(openToken, offset); } if (offset < src.length) { builder.append(src, offset, src.length - offset); } } return new MapedSql(builder.toString(), paramters); } public static interface TokenHandler { String handleToken(String content, List paramters); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/token/ReplaceTokenHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql.token; import java.util.Map; import java.util.function.Function; public final class ReplaceTokenHandler implements StringTokenParser.TokenHandler { private final Function valueHandler; public static ReplaceTokenHandler createByMap(Map map) { return new ReplaceTokenHandler(new MapValueHandler(map)); } public static ReplaceTokenHandler create(Function valueHandler) { return new ReplaceTokenHandler(valueHandler); } public ReplaceTokenHandler(Function valueHandler) { this.valueHandler = valueHandler; } @Override public String handleToken(String content) { Object v = valueHandler.apply(content); if (v == null) { return null; } return v.toString(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/token/StringTokenParser.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql.token; public class StringTokenParser { private final String openToken; private final String closeToken; private final TokenHandler handler; public StringTokenParser(String openToken, String closeToken, TokenHandler handler) { this.openToken = openToken; this.closeToken = closeToken; this.handler = handler; } public String parse(String text) { StringBuilder builder = new StringBuilder(); if (text != null && text.length() > 0) { char[] src = text.toCharArray(); int offset = 0; int start = text.indexOf(openToken, offset); while (start > -1) { if (start > 0 && src[start - 1] == '\\') { builder.append(src, offset, start - offset - 1).append(openToken); offset = start + openToken.length(); } else { int end = text.indexOf(closeToken, start); if (end == -1) { builder.append(src, offset, src.length - offset); offset = src.length; } else { builder.append(src, offset, start - offset); offset = start + openToken.length(); String content = new String(src, offset, end - offset); String value = handler.handleToken(content); builder.append(value != null ? value : this.openToken + content + this.closeToken); offset = end + closeToken.length(); } } start = text.indexOf(openToken, offset); } if (offset < src.length) { builder.append(src, offset, src.length - offset); } } return builder.toString(); } public static interface TokenHandler { String handleToken(String content); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sql/token/VariableTokenHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sql.token; import java.util.List; import java.util.Map; import java.util.function.Function; public class VariableTokenHandler implements MapedSqlTokenParser.TokenHandler { private final Function valueHandler; public static VariableTokenHandler createByMap(Map map) { return new VariableTokenHandler(new MapValueHandler(map)); } public static VariableTokenHandler create(Function valueHandler) { return new VariableTokenHandler(valueHandler); } public VariableTokenHandler(Function valueHandler) { this.valueHandler = valueHandler; } @Override public String handleToken(String content, List paramters) { paramters.add(valueHandler.apply(content)); return "?"; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sumk/batis/ConfigurationFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sumk.batis; import org.apache.ibatis.session.Configuration; import org.yx.base.Ordered; public interface ConfigurationFactory extends Ordered { Configuration create(String name); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sumk/batis/ProxySession.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sumk.batis; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.Map; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import org.yx.db.conn.ConnectionPool; import org.yx.db.enums.DBType; import org.yx.exception.SumkException; import org.yx.log.Log; public class ProxySession implements SqlSession { @Override public Connection getConnection() { throw new SumkException(148675, "getConnection not support"); } protected SqlSession readSession() { ConnectionPool context = ConnectionPool.get(); try { return SqlSessionFactory.get(context.getDbName()).openSession(context.connection(DBType.READ_PREFER)); } catch (SQLException e) { Log.get("sumk.mybatis").error(e.getMessage(), e); throw new SumkException(345254, "创建mybatis的读session失败"); } } protected SqlSession writeSession() { ConnectionPool context = ConnectionPool.get(); try { return SqlSessionFactory.get(context.getDbName()).openSession(context.connection(DBType.WRITE)); } catch (SQLException e) { Log.get("sumk.mybatis").error(e.getMessage(), e); throw new SumkException(345255, "创建mybatis的写session失败"); } } @Override public T selectOne(String statement) { return this.readSession().selectOne(statement); } @Override public T selectOne(String statement, Object parameter) { return this.readSession().selectOne(statement, parameter); } @Override public Map selectMap(String statement, String mapKey) { return this.readSession().selectMap(statement, mapKey); } @Override public Map selectMap(String statement, Object parameter, String mapKey) { return this.readSession().selectMap(statement, parameter, mapKey); } @Override public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { return this.readSession().selectMap(statement, parameter, mapKey, rowBounds); } @Override public List selectList(String statement) { return this.readSession().selectList(statement); } @Override public List selectList(String statement, Object parameter) { return this.readSession().selectList(statement, parameter); } @Override public List selectList(String statement, Object parameter, RowBounds rowBounds) { return this.readSession().selectList(statement, parameter, rowBounds); } @SuppressWarnings("rawtypes") @Override public void select(String statement, Object parameter, ResultHandler handler) { this.readSession().select(statement, parameter, handler); } @SuppressWarnings("rawtypes") @Override public void select(String statement, ResultHandler handler) { this.readSession().select(statement, handler); } @SuppressWarnings("rawtypes") @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { this.readSession().select(statement, parameter, rowBounds, handler); } @Override public int insert(String statement) { return this.writeSession().insert(statement); } @Override public int insert(String statement, Object parameter) { return this.writeSession().insert(statement, parameter); } @Override public int update(String statement) { return this.writeSession().update(statement); } @Override public int update(String statement, Object parameter) { return this.writeSession().update(statement, parameter); } @Override public int delete(String statement) { return this.writeSession().delete(statement); } @Override public int delete(String statement, Object parameter) { return this.writeSession().delete(statement, parameter); } @Override public void commit() { } @Override public void commit(boolean force) { } @Override public void rollback() { } @Override public void rollback(boolean force) { } @Override public List flushStatements() { return this.writeSession().flushStatements(); } @Override public void close() { } @Override public Configuration getConfiguration() { return this.writeSession().getConfiguration(); } @Override public T getMapper(Class type) { return this.writeSession().getMapper(type); } @Override public void clearCache() { this.writeSession().clearCache(); } @Override public Cursor selectCursor(String statement) { return this.readSession().selectCursor(statement); } @Override public Cursor selectCursor(String statement, Object parameter) { return this.readSession().selectCursor(statement, parameter); } @Override public Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds) { return this.readSession().selectCursor(statement, parameter, rowBounds); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sumk/batis/SqlSessionFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sumk.batis; import java.io.ByteArrayInputStream; import java.sql.Connection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.defaults.DefaultSqlSession; import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.transaction.managed.ManagedTransaction; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.conf.LocalMultiResourceLoaderSupplier; import org.yx.conf.MultiResourceLoader; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.util.StringUtil; public class SqlSessionFactory { private static final ConcurrentMap factoryMap = new ConcurrentHashMap<>(); private Configuration configuration; private String db; private SqlSessionFactory() { } private static SqlSessionFactory create(String dbName) throws Exception { SqlSessionFactory sessionFactory = new SqlSessionFactory(); sessionFactory.db = dbName; List confFactorys = IOC.getBeans(ConfigurationFactory.class); if (confFactorys != null && confFactorys.size() > 0) { for (ConfigurationFactory f : confFactorys) { Configuration conf = f.create(dbName); if (conf != null) { sessionFactory.configuration = conf; return sessionFactory.sqlParse(); } } } Configuration conf = new Configuration(); conf.setDefaultExecutorType(ExecutorType.SIMPLE); conf.setCacheEnabled(false); sessionFactory.configuration = conf; return sessionFactory.sqlParse(); } private static Supplier resourceLoader = new LocalMultiResourceLoaderSupplier( AppInfo.get("sumk.db.mybatis.path", AppInfo.CLASSPATH_URL_PREFIX + "batis")); public static void setResourceLoader(Supplier resourceLoader) { SqlSessionFactory.resourceLoader = Objects.requireNonNull(resourceLoader); } public static Supplier getResourceLoader() { return resourceLoader; } public static SqlSessionFactory get(String dbName) { SqlSessionFactory factory = factoryMap.get(dbName); if (factory != null) { return factory; } try { factory = factoryMap.computeIfAbsent(dbName, name -> { Logs.db().info("mybatis创建{}的SqlSessionFactory", name); try { return SqlSessionFactory.create(name); } catch (Exception e) { Logs.db().error("创建" + name + "的SqlSessionFactory失败", e); return null; } }); if (factory != null) { return factory; } return factoryMap.get(dbName); } catch (Exception e) { Log.printStack("sumk.sql.error", e); throw new SumkException(100234325, dbName + " create SqlSessionFactory failed"); } } void destroy() { } public static void reload(String dbName) throws Exception { dbName = StringUtil.requireNotEmpty(dbName).trim(); SqlSessionFactory factory = factoryMap.get(dbName); if (factory == null) { return; } factory = SqlSessionFactory.create(dbName); SqlSessionFactory old = factoryMap.put(dbName, factory); old.destroy(); } public SqlSession openSession(Connection conn) { Transaction transaction = new ManagedTransaction(conn, false); Executor executor = configuration.newExecutor(transaction); return new DefaultSqlSession(configuration, executor); } SqlSessionFactory sqlParse() throws Exception { Map sqls = resourceLoader.get().openResources(db); Set> entries = sqls.entrySet(); for (Entry entry : entries) { byte[] bs = entry.getValue(); XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(new ByteArrayInputStream(bs), configuration, entry.getKey(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } return this; } public Configuration getConfiguration() { return this.configuration; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/sumk/batis/SqlSessionHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.sumk.batis; import org.apache.ibatis.session.SqlSession; /** * 不要作为全局变量,要作为方法的局部变量,要在@Box里面调用 * 因为数据库连接池重写了connection.close(),所以可以尽情的调用session.close()
* 供外部调用的类 */ public abstract class SqlSessionHolder { public static SqlSession session() { return new ProxySession(); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/Exchange.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import org.yx.db.sql.PojoMeta; import org.yx.log.Log; import org.yx.redis.RedisPool; import org.yx.util.StringUtil; public class Exchange { private List> leftIn = Collections.emptyList(); private List data; public Exchange() { } public void setLeftIn(List> leftIn) { this.leftIn = Objects.requireNonNull(leftIn); } public List> getLeftIn() { return this.leftIn; } public List getData() { return data; } public void findFromCache(PojoMeta pm) { List> origin = this.leftIn; if (origin == null || origin.isEmpty() || RedisPool.defaultRedis() == null) { return; } try { List redisList = new ArrayList<>(origin.size()); List> redisConditions = new ArrayList<>(origin.size()); List> notFound = new ArrayList<>(origin.size()); for (Map map : origin) { if (pm.isOnlyCacheID(map)) { redisList.add(pm.getCacheID(map, false)); redisConditions.add(map); } else { notFound.add(map); } } if (redisList.isEmpty()) { return; } List redisData = RecordRepository.getMultiValue(pm, redisList); if (redisData == null || redisData.isEmpty()) { return; } this.data = new ArrayList<>(redisData.size()); for (int i = 0; i < redisConditions.size(); i++) { Map conditon = redisConditions.get(i); if (i < redisData.size() && StringUtil.isNotEmpty(redisData.get(i))) { this.data.add(redisData.get(i)); continue; } notFound.add(conditon); } this.leftIn = notFound.isEmpty() ? null : notFound; } catch (Exception e) { this.data = null; Log.printStack("sumk.sql", e); } } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/MapResultHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.yx.db.DBJson; import org.yx.db.enums.CacheType; import org.yx.db.sql.ColumnMeta; import org.yx.db.sql.PojoMeta; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class MapResultHandler implements ResultHandler { public static final MapResultHandler handler = new MapResultHandler(); public static Map filterSelectColumns(Map map, List selectColumns) { if (selectColumns.isEmpty()) { return map; } Iterator it = map.keySet().iterator(); while (it.hasNext()) { String key = it.next(); if (!selectColumns.contains(key)) { it.remove(); } } return map; } @SuppressWarnings("unchecked") @Override public List parseFromJson(PojoMeta pm, List jsons, List selectColumns) throws InstantiationException, IllegalAccessException { if (CollectionUtil.isEmpty(jsons)) { return null; } List> list = new ArrayList<>(); for (String json : jsons) { if (StringUtil.isEmpty(json)) { continue; } if (pm.cacheType() == CacheType.LIST || (json.startsWith("[") && json.endsWith("]"))) { Object[] ts = DBJson.operator().fromJson(json, pm.pojoArrayClz()); if (ts == null || ts.length == 0) { continue; } for (Object obj : ts) { Map map = pm.populate(obj, false); map = filterSelectColumns(map, selectColumns); if (map.size() > 0) { list.add(map); } } continue; } Object obj = DBJson.operator().fromJson(json, pm.pojoClz()); if (obj == null) { continue; } Map map = pm.populate(obj, false); map = filterSelectColumns(map, selectColumns); if (map.size() > 0) { list.add(map); } } return (List) list; } @SuppressWarnings("unchecked") @Override public List parse(PojoMeta pm, List> list) { if (list == null || list.isEmpty()) { return Collections.emptyList(); } List> ret = new ArrayList<>(list.size()); for (Map m0 : list) { Map map = new HashMap<>(); for (Entry en : m0.entrySet()) { map.put(en.getKey().getFieldName(), en.getValue()); } ret.add(map); } return (List) ret; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/PojoResultHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.yx.db.DBJson; import org.yx.db.enums.CacheType; import org.yx.db.sql.ColumnMeta; import org.yx.db.sql.PojoMeta; import org.yx.util.CollectionUtil; import org.yx.util.Loader; import org.yx.util.StringUtil; public class PojoResultHandler implements ResultHandler { public static final PojoResultHandler handler = new PojoResultHandler(); public void filterSelectColumns(PojoMeta pm, Object obj, List selectColumns) throws Exception { if (selectColumns.isEmpty()) { return; } for (ColumnMeta cm : pm.fieldMetas()) { if (!selectColumns.contains(cm.getFieldName())) { cm.getField().set(obj, null); } } } @SuppressWarnings("unchecked") @Override public List parseFromJson(PojoMeta pm, List jsons, List selectColumns) throws Exception { if (CollectionUtil.isEmpty(jsons)) { return null; } List list = new ArrayList<>(jsons.size()); for (String json : jsons) { if (StringUtil.isEmpty(json)) { continue; } if (pm.cacheType() == CacheType.LIST || (json.startsWith("[") && json.endsWith("]"))) { Object[] ts = DBJson.operator().fromJson(json, pm.pojoArrayClz()); if (ts == null || ts.length == 0) { continue; } for (Object t : ts) { this.filterSelectColumns(pm, t, selectColumns); list.add(t); } continue; } Object obj = DBJson.operator().fromJson(json, pm.pojoClz()); if (obj == null) { continue; } this.filterSelectColumns(pm, obj, selectColumns); list.add(obj); } return (List) list; } @SuppressWarnings("unchecked") @Override public List parse(PojoMeta pm, List> list) throws Exception { List ret = new ArrayList<>(); for (Map map : list) { if (map.isEmpty()) { continue; } ret.add(buildPojo(pm, map)); } return (List) ret; } private Object buildPojo(PojoMeta pm, Map map) throws Exception { Object obj = Loader.newInstance(pm.pojoClz()); for (Entry en : map.entrySet()) { if (en.getValue() == null) { continue; } en.getKey().setValue(obj, en.getValue()); } return obj; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/RecordAccess.java ================================================ package org.yx.db.visit; import java.util.Collection; import java.util.List; import org.yx.db.sql.PojoMeta; /** * 本接口只负责实际的存取,实现类一般不需要null判断等 */ public interface RecordAccess { String get(PojoMeta m, String id); List getMultiValue(PojoMeta m, Collection ids); void set(PojoMeta m, String id, String json); void del(PojoMeta m, String id); void delMulti(PojoMeta m, String[] ids); } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/RecordRepository.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import org.yx.db.sql.PojoMeta; import org.yx.exception.SumkException; public final class RecordRepository { private static RecordAccess access = new RedisAccess(); public static RecordAccess access() { return access; } public static void setAccess(RecordAccess access) { RecordRepository.access = Objects.requireNonNull(access); } public static String get(PojoMeta m, String id) { if (!m.getCounter().willVisitCache(1)) { return null; } String s = access.get(m, id); if (s != null) { m.getCounter().incrCacheHit(1); } return s; } public static void set(PojoMeta m, String id, String json) { if (json == null) { return; } if (id == null || id.isEmpty()) { throw new SumkException(657645465, "key of redis value cannot be null"); } access.set(m, id, json); } public static void del(PojoMeta m, String id) { access.del(m, id); } public static void delMulti(PojoMeta m, String[] ids) { if (ids == null || ids.length == 0) { return; } access.delMulti(m, ids); } public static List getMultiValue(PojoMeta m, Collection ids) { if (ids == null || ids.isEmpty()) { return Collections.emptyList(); } if (!m.getCounter().willVisitCache(ids.size())) { return Collections.emptyList(); } List ret = access.getMultiValue(m, ids); if (ret == null || ret.isEmpty()) { return Collections.emptyList(); } if (ret != null && ret.size() > 0) { int c = 0; for (String v : ret) { if (v != null) { c++; } } m.getCounter().incrCacheHit(c); } return ret; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/RedisAccess.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.slf4j.Logger; import org.yx.db.sql.PojoMeta; import org.yx.log.Log; import org.yx.redis.Redis; import org.yx.redis.RedisPool; public final class RedisAccess implements RecordAccess { private static Logger logger = Log.get("sumk.redis.access"); protected Redis muteRedis(String tableName) { return RedisPool.get(tableName).mute(); } protected String getKey(PojoMeta m, String id) { return m.getPre() + id; } protected String[] getKeys(PojoMeta m, String[] ids) { String[] keys = new String[ids.length]; for (int i = 0; i < ids.length; i++) { keys[i] = getKey(m, ids[i]); } return keys; } @Override public String get(PojoMeta m, String id) { return muteRedis(m.getTableName()).get(getKey(m, id)); } @Override public void set(PojoMeta m, String id, String json) { String key = getKey(m, id); String tableName = m.getTableName(); int ttl = m.getTtlSec(); if (ttl <= 0) { muteRedis(tableName).set(key, json); if (logger.isTraceEnabled()) { logger.trace("{} >> SET {} = {}", tableName, key, json); } return; } muteRedis(tableName).setex(key, ttl, json); if (logger.isTraceEnabled()) { logger.trace("{} >> SETEX {} = {}", tableName, key, json); } } @Override public void del(PojoMeta m, String id) { String key = getKey(m, id); String tableName = m.getTableName(); muteRedis(tableName).del(key); if (logger.isTraceEnabled()) { logger.trace("{} >> DELETE {}", tableName, key); } } @Override public void delMulti(PojoMeta m, String[] ids) { String[] keys = getKeys(m, ids); muteRedis(m.getTableName()).del(keys); if (logger.isTraceEnabled()) { String ks = Arrays.toString(keys); ks = ks.substring(1, ks.length() - 1); logger.trace("{} >> DEL_MULTI {}", m.getTableName(), ks); } } @Override public List getMultiValue(PojoMeta m, Collection ids) { String[] keys = getKeys(m, ids.toArray(new String[ids.size()])); return muteRedis(m.getTableName()).mget(keys); } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/ResultHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.util.List; import java.util.Map; import org.yx.db.sql.ColumnMeta; import org.yx.db.sql.PojoMeta; /** * 处理结果要包含所有的原始字段,并且返回值的toJson()也不能有字段损失 */ public interface ResultHandler { List parseFromJson(PojoMeta pm, List jsons, List selectColumns) throws Exception; List parse(PojoMeta pm, List> list) throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/ResultSetUtils.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.yx.base.sumk.map.ListMap; import org.yx.common.util.kit.Asserts; import org.yx.db.sql.ColumnMeta; import org.yx.db.sql.PojoMeta; import org.yx.exception.SumkException; public final class ResultSetUtils { public static List> toMapList(ResultSet rs) throws java.sql.SQLException { List> list = new ArrayList<>(10); if (rs == null) { return list; } ResultSetMetaData md = rs.getMetaData(); int columnCount = md.getColumnCount(); Map rowData; while (rs.next()) { rowData = new HashMap<>(); for (int i = 1; i <= columnCount; i++) { rowData.put(md.getColumnName(i), rs.getObject(i)); } list.add(rowData); } return list; } public static List> toMapList(ResultSet rs, PojoMeta pm) throws java.sql.SQLException { List> list = new ArrayList<>(); if (rs == null) { return list; } ResultSetMetaData md = rs.getMetaData(); int columnCount = md.getColumnCount(); ColumnMeta[] columns = new ColumnMeta[columnCount + 1]; for (int i = 1; i <= columnCount; i++) { ColumnMeta cm = pm.getByColumnDBName(md.getColumnName(i)); if (cm == null) { throw new SumkException(629234, md.getColumnName(i) + "这个字段没有在java的pojo类中定义"); } columns[i] = cm; } Map rowData; while (rs.next()) { rowData = new ListMap<>(columnCount); for (int i = 1; i <= columnCount; i++) { rowData.put(columns[i], rs.getObject(i)); } list.add(rowData); } rs.close(); return list; } public static List toList(ResultSet rs) throws java.sql.SQLException { List list = new ArrayList<>(10); if (rs == null) { return list; } ResultSetMetaData md = rs.getMetaData(); Asserts.requireTrue(md.getColumnCount() == 1, "result data column is " + md.getColumnCount() + ", not 1"); while (rs.next()) { list.add(rs.getObject(1)); } rs.close(); return list; } public static List toObjectArrayList(ResultSet rs) throws java.sql.SQLException { List list = new ArrayList<>(10); if (rs == null) { return list; } ResultSetMetaData md = rs.getMetaData(); final int len = md.getColumnCount(); Object[] data; while (rs.next()) { data = new Object[len]; for (int i = 0; i < len; i++) { data[i] = rs.getObject(i + 1); } list.add(data); } rs.close(); return list; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/SumkDbVisitor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import org.yx.db.sql.SqlBuilder; public interface SumkDbVisitor { T visit(SqlBuilder builder) throws Exception; } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/SumkStatement.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import org.slf4j.Logger; import org.yx.db.kit.DBKits; import org.yx.db.sql.DBSettings; import org.yx.db.sql.MapedSql; import org.yx.db.sql.SqlLog; import org.yx.exception.SumkException; import org.yx.exception.SumkExceptionCode; import org.yx.log.CodeLineMarker; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.util.SumkDate; public class SumkStatement implements AutoCloseable { private static final Logger LOG = Log.get("sumk.sql.plain"); private static long executeCount; private static SqlLog sqlLog = (a, b, c) -> { }; private static CodeLineMarker marker = new CodeLineMarker("org.yx.db."); private static BiConsumer> statementParamAttacher = (ps, params) -> { int size = params.size(); if (size == 0) { return; } try { for (int i = 0; i < size; i++) { Object parameterObj = params.get(i); if (parameterObj == null) { ps.setNull(i + 1, java.sql.Types.OTHER); continue; } if (parameterObj.getClass() == java.util.Date.class) { ps.setTimestamp(i + 1, new Timestamp(((java.util.Date) parameterObj).getTime())); continue; } if (parameterObj.getClass() == SumkDate.class) { ps.setTimestamp(i + 1, ((SumkDate) parameterObj).toTimestamp()); continue; } ps.setObject(i + 1, parameterObj); } } catch (Exception e) { throw new SumkException(3643654, "设置PreparedStatement的参数失败,sql语句: " + DBKits.getSqlOfStatement(ps) + " ,参数: " + params, e); } }; public static BiConsumer> getStatementParamAttacher() { return statementParamAttacher; } public static void setStatementParamAttacher(BiConsumer> statementParamAttacher) { SumkStatement.statementParamAttacher = Objects.requireNonNull(statementParamAttacher); } private final AtomicReference statement; private final MapedSql maped; private final long beginTime; private int sqlTime; private int modifyCount = -1; private Throwable exception; /** * 非空 * * @return marker实例 */ public static CodeLineMarker getMarker() { return marker; } public static void setMarker(CodeLineMarker marker) { SumkStatement.marker = Objects.requireNonNull(marker); } public static void setSqlLog(SqlLog sqlLog) { SumkStatement.sqlLog = Objects.requireNonNull(sqlLog); } static SumkStatement create(Connection conn, MapedSql maped) throws Exception { return new SumkStatement(conn.prepareStatement(maped.getSql()), maped); } static SumkStatement createAutoGenerateKeyStatement(Connection conn, MapedSql maped) throws Exception { return new SumkStatement(conn.prepareStatement(maped.getSql(), Statement.RETURN_GENERATED_KEYS), maped); } private SumkStatement(PreparedStatement statement, MapedSql maped) { this.statement = new AtomicReference<>(statement); this.maped = maped; beginTime = System.currentTimeMillis(); attachParams(); } private PreparedStatement checkStatement() throws SQLException { PreparedStatement statement = this.statement.get(); if (statement == null) { throw new SumkException(SumkExceptionCode.DB_CONNECTION_CLOSED, "连接已关闭"); } executeCount++; return statement; } public int executeUpdate() throws SQLException { PreparedStatement statement = checkStatement(); modifyCount = 0; try { modifyCount = statement.executeUpdate(); } catch (Exception e) { this.exception = e; sqlTime = (int) (System.currentTimeMillis() - beginTime); throw e; } sqlTime = (int) (System.currentTimeMillis() - beginTime); return modifyCount; } public ResultSet executeQuery() throws Exception { PreparedStatement statement = checkStatement(); ResultSet ret = null; try { ret = statement.executeQuery(); } catch (Exception e) { this.exception = e; sqlTime = (int) (System.currentTimeMillis() - beginTime); throw e; } sqlTime = (int) (System.currentTimeMillis() - beginTime); return ret; } @Override public void close() throws SQLException { PreparedStatement statement = this.statement.getAndSet(null); if (statement == null) { return; } try { if (this.exception != null) { LOG.error(marker, DBKits.getSqlOfStatement(statement) + ", 发生异常", this.exception); } statement.close(); int totalTime = (int) (System.currentTimeMillis() - beginTime); if (DBSettings.isUnionLogEnable() && (this.exception != null || totalTime >= DBSettings.unionLogTime())) { sqlLog.log(this, totalTime, this.exception); } this.logSpendTime(); } catch (SQLException e1) { Logs.db().error(e1.getMessage(), e1); } } private void logSpendTime() { if (LOG.isDebugEnabled()) { if (this.sqlTime > DBSettings.debugLogSpendTime()) { LOG.debug(marker, this.buildTimeLog()); } else if (LOG.isTraceEnabled()) { LOG.trace(marker, this.buildTimeLog()); } } } private String buildTimeLog() { StringBuilder sb = new StringBuilder(); sb.append("$耗时(ms):").append(this.sqlTime); if (this.modifyCount >= 0) { sb.append(",修改的记录数:").append(this.modifyCount); } return sb.toString(); } private void attachParams() { PreparedStatement statement = this.statement.get(); if (statement == null) { throw new SumkException(SumkExceptionCode.DB_CONNECTION_CLOSED, "连接已关闭了"); } statementParamAttacher.accept(statement, maped.getParamters()); if (LOG.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); LOG.debug(marker, sb.append("<=> ").append(DBKits.getSqlOfStatement(this.statement.get())).toString()); } } public ResultSet getGeneratedKeys() throws SQLException { PreparedStatement statement = this.statement.get(); if (statement == null) { throw new SumkException(SumkExceptionCode.DB_CONNECTION_CLOSED, "连接已关闭"); } return statement.getGeneratedKeys(); } public int getSqlTime() { return sqlTime; } public int getModifyCount() { return modifyCount; } public MapedSql getMaped() { return maped; } public static long getExecuteCount() { return executeCount; } } ================================================ FILE: sumk-db/src/main/java/org/yx/db/visit/Visitors.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.db.visit; import java.sql.Connection; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.yx.db.conn.ConnectionPool; import org.yx.db.enums.DBType; import org.yx.db.event.DBEvent; import org.yx.db.event.ModifyEvent; import org.yx.db.sql.ColumnMeta; import org.yx.db.sql.DBSettings; import org.yx.db.sql.InsertResult; import org.yx.db.sql.MapedSql; import org.yx.db.sql.PojoMeta; import org.yx.db.sql.SelectBuilder; import org.yx.db.sql.SqlBuilder; public final class Visitors { public static interface Transform { T transFrom(ResultSet ret) throws Exception; } public static class QueryVisitor implements SumkDbVisitor { private final Transform transform; private QueryVisitor(Transform transform) { this.transform = transform; } @Override public T visit(SqlBuilder builder) throws Exception { MapedSql maped = builder.toMapedSql(); Connection conn = ConnectionPool.get().connection(DBSettings.readType()); try (SumkStatement statement = SumkStatement.create(conn, maped)) { ResultSet ret = statement.executeQuery(); T list = this.transform.transFrom(ret); return list; } } } public static final SumkDbVisitor modifyVisitor = builder -> { ConnectionPool pool = ConnectionPool.get(); MapedSql maped = builder.toMapedSql(); int ret; try (SumkStatement statement = SumkStatement.create(pool.connection(DBType.WRITE), maped)) { ret = statement.executeUpdate(); } if (ret > 0) { DBEvent md = maped.getEvent(); if (md instanceof ModifyEvent) { ModifyEvent me = (ModifyEvent) md; me.setAffected(ret); } pool.pubuishModify(md); } return ret; }; public static final SumkDbVisitor insertWithAutoGeneratedKeysVisitor = builder -> { Connection conn = ConnectionPool.get().connection(DBType.WRITE); MapedSql maped = builder.toMapedSql(); try (SumkStatement statement = SumkStatement.createAutoGenerateKeyStatement(conn, maped)) { int count = statement.executeUpdate(); ResultSet rs = statement.getGeneratedKeys(); List keys = new ArrayList<>(); InsertResult result = new InsertResult(count, keys); if (rs != null && rs.getMetaData().getColumnCount() > 0) { if (rs.next()) { keys.add(rs.getLong(1)); } } rs.close(); return result; } }; public static final SumkDbVisitor>> queryVisitorForORM = builder -> { MapedSql maped = builder.toMapedSql(); Connection conn = ConnectionPool.get().connection(DBSettings.readType()); try (SumkStatement statement = SumkStatement.create(conn, maped)) { ResultSet ret = statement.executeQuery(); PojoMeta pm = ((SelectBuilder) builder).makeSurePojoMeta(); return ResultSetUtils.toMapList(ret, pm); } }; public static final SumkDbVisitor>> queryVisitor = new QueryVisitor<>( ResultSetUtils::toMapList); public static final SumkDbVisitor> singleListQueryVisitor = new QueryVisitor<>(ResultSetUtils::toList); public static final SumkDbVisitor> arrayListQueryVisitor = new QueryVisitor<>( ResultSetUtils::toObjectArrayList); } ================================================ FILE: sumk-db/src/main/resources/META-INF/sql.dtd ================================================ ================================================ FILE: sumk-db/src/main/resources/META-INF/sumk.factories ================================================ sumk.ioc.bean=org.yx.db.conn.CommonConfigFactory,\ org.yx.db.exec.BoxAopExecutorSupplier,\ org.yx.db.listener.DeleteListener,\ org.yx.db.listener.InsertListener,\ org.yx.db.listener.SelectListener,\ org.yx.db.listener.UpdateListener,\ org.yx.db.mapper.DBPlugin sumk.ioc.boot.watcher=org.yx.db.sql.TableBootWatcher ================================================ FILE: sumk-framework/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: sumk-framework/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-framework com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots com.github.youtongluan sumk-base ${project.version} org.ow2.asm asm redis.clients jedis junit junit test ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/Bean.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Bean { /** * @return 自定义bean的名称。慎用此方法 */ String value() default ""; } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/ConditionOnProperty.java ================================================ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 指定的属性名存在时该bean才会被初始化。属性指定是可以通过StartContext获取的属性。
* 可以使用,或&&来表示依赖多个属性。用||来表示其中任何一个属性存在就可以初始化 */ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ConditionOnProperty { /** * 可以用逗号或者||分隔。前者表示并且,后者表示或者 * * @return StartContext或者AppInfo配置中的key */ String value(); /** * 默认情况下属性存在@Bean才有效,如果本方法返回false,那么属性不存在的时候,@Bean才有效 * * @return 当onMatch和表达式的匹配结果一致时,条件成立 */ boolean onMatch() default true; } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/Exclude.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 本注解注释的接口,不会被@SoaClient解析。
* 本注解注释的pojo字段,不会被映射到数据库。 * 跟transient关键字效果类似,但是transient会影响S.json()、S.bean(),但是本注解不会 */ @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Exclude { } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/ExcludeFromParams.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 表示不作为参数。它对http和rpc的参数起作用 */ @Target({ ElementType.FIELD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExcludeFromParams { } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/ExcludeFromResponse.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 表示不作为响应内容。它对http和rpc的参数起作用 */ @Target({ ElementType.FIELD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExcludeFromResponse { } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/Inject.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 相当于spring的AutoWired,使用在全局变量上.它只会注入当前值为null的字段。
* 下列三种方式优先级从高到低注入对象,只有在上一种方式无法获取对象的时候,才使用下一种方式注入:
* 1、使用name和clz获取对象
* 2、使用name获取对象
* 3、使用clz获取对象
* * @author 游夏 * */ @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Inject { /** * 如果设置了这个属性,会强制使用这个值去寻找bean,忽略掉智能推断。 该属性对数组或集合类型的注入无效 * * @return 要注入的bean的名称 */ String value() default ""; /** * 数组、集合类型不可能为null,但是可以为空 * * @return 如果为true,表示可以为null或空集合 */ boolean allowEmpty() default false; /** * 是否允许存在多个bean。设置为true的时候,最好和Ordered接口一起使用 * * @return 如果设置为true,存在多个bean的时候,返回第一个 */ boolean allowMulti() default false; } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/Param.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.PARAMETER, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Param { /** * @return 中文名称 * */ String value() default ""; /** * 为了调试方便,可以在启动的时候设置sumk.param.required.enable=0来禁用它 * * @return true表示是必传参数,不允许为null或空字符串 */ boolean required() default true; /** * 数字类型支持int、long、byte、short、Integer、Long、Byte、Short * * @return 字符串的最大长度或正整数的最大值(包含),小于0表示不限 */ int max() default -1; /** * @return 字符串的最小长度或正整数的最小值(包含),小于0表示不限 */ int min() default -1; /** * @return 文档中的字段示例,也可用于扩展 */ String example() default ""; /** * @return 文档中的备注信息,也可用于扩展 */ String comment() default ""; /** * 只有本属性为true的参数,才会校验内部字段
* 不支持泛型,包括集合类型。 * * @return true表示该参数或字段是复合类型,会对内部的字段进行校验 */ boolean complex() default false; } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/Priority.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Target({ TYPE }) @Retention(RUNTIME) @Documented public @interface Priority { /** * bean解析时的优先级,值越小越靠前 * * @return 如果没有用这个注解,默认为Const.DEFAULT_ORDER */ int value(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/spec/BeanSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.spec; public class BeanSpec { private final String value; private final String conditionOnProperty; /** * conditionOnProperty不为空的时候,本属性才有意义 */ private final boolean onProperty; public BeanSpec(String value, String conditionOnProperty, boolean onProperty) { this.value = value; this.conditionOnProperty = conditionOnProperty; this.onProperty = onProperty; } public String value() { return this.value; } public String conditionOnProperty() { return this.conditionOnProperty; } public boolean onProperty() { return onProperty; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/spec/BuiltInParsers.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.spec; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.function.BiFunction; import java.util.function.Function; import org.yx.annotation.Bean; import org.yx.annotation.ConditionOnProperty; import org.yx.annotation.Inject; import org.yx.annotation.Param; import org.yx.util.StringUtil; public final class BuiltInParsers { public static final Function, BeanSpec> BEAN_PARSER = clz -> { Bean c = clz.getAnnotation(Bean.class); if (c == null) { return null; } ConditionOnProperty p = clz.getAnnotation(ConditionOnProperty.class); if (p == null) { return new BeanSpec(c.value(), null, true); } return new BeanSpec(c.value(), StringUtil.toLatin(p.value()), p.onMatch()); }; public static final BiFunction INJECT_PARSER = (src, f) -> { Inject c = f.getAnnotation(Inject.class); if (c == null) { return null; } return new InjectSpec(c.value(), c.allowEmpty(), c.allowMulti()); }; public static final Function PARAM_FIELD_PARSER = f -> { Param c = f.getAnnotation(Param.class); if (c == null) { return null; } return createParamSpec(c); }; public static final Function PARAM_PARAMTER_PARSER = m -> { Annotation[][] paramAnno = m.getParameterAnnotations(); ParamSpec[] params = new ParamSpec[paramAnno.length]; for (int i = 0; i < paramAnno.length; i++) { Annotation[] a = paramAnno[i]; for (Annotation a2 : a) { if (Param.class == a2.annotationType()) { params[i] = createParamSpec((Param) a2); break; } } } return params; }; public static ParamSpec createParamSpec(Param p) { return new ParamSpec(p.value(), p.required(), p.max(), p.min(), p.example(), p.comment(), p.complex(), null); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/spec/InjectSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.spec; public class InjectSpec { private final boolean allowEmpty; private final boolean allowMulti; private final String value; public InjectSpec(String value, boolean allowEmpty, boolean allowMulti) { this.value = value; this.allowEmpty = allowEmpty; this.allowMulti = allowMulti; } public String value() { return value; } /** * 数组、集合类型不可能为null,但是可以为空 * * @return 如果为true,表示可以为null或空集合 */ public boolean allowEmpty() { return this.allowEmpty; } public boolean allowMulti() { return allowMulti; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/spec/ParamSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.spec; import java.util.Objects; public class ParamSpec { private final String value; private final boolean required; private final int max; private final int min; private final String example; private final String comment; private final boolean complex; private final Object custom; public ParamSpec(String value, boolean required, int max, int min, String example, String comment, boolean complex, Object custom) { this.value = Objects.requireNonNull(value); this.required = required; this.max = max; this.min = min; this.example = example; this.comment = comment; this.complex = complex; this.custom = custom; } /** * @return 中文名称 * */ public String value() { return this.value; } public boolean required() { return this.required; } public int max() { return this.max; } public int min() { return this.min; } public String example() { return this.example; } public String comment() { return this.comment; } public boolean complex() { return this.complex; } /** * 这个属性是留给开发者自己扩展用 * * @return 这个属性尽量不要返回空字符串,应用null代替空字符串 */ public Object custom() { return this.custom; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/spec/SpecParsers.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.spec; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; public final class SpecParsers { private static Function, BeanSpec> beanParser = BuiltInParsers.BEAN_PARSER; private static BiFunction injectParser = BuiltInParsers.INJECT_PARSER; private static Function paramFieldParser = BuiltInParsers.PARAM_FIELD_PARSER; private static Function paramParamterParser = BuiltInParsers.PARAM_PARAMTER_PARSER; public static Function, BeanSpec> getBeanParser() { return beanParser; } public static BiFunction getInjectParser() { return injectParser; } public static Function getParamFieldParser() { return paramFieldParser; } public static Function getParamParamterParser() { return paramParamterParser; } public static void setBeanParser(Function, BeanSpec> beanParser) { SpecParsers.beanParser = Objects.requireNonNull(beanParser); } public static void setInjectParser(BiFunction injectParser) { SpecParsers.injectParser = Objects.requireNonNull(injectParser); } public static void setParamFieldParser(Function paramFieldParser) { SpecParsers.paramFieldParser = Objects.requireNonNull(paramFieldParser); } public static void setParamParamterParser(Function paramParamterParser) { SpecParsers.paramParamterParser = Objects.requireNonNull(paramParamterParser); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/annotation/spec/Specs.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.spec; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.function.BiFunction; import java.util.function.Function; public class Specs { public static BeanSpec extractBean(Class clz) { return parse(clz, SpecParsers.getBeanParser()); } public static InjectSpec extractInject(Object destBean, Field f) { return parse(destBean, f, SpecParsers.getInjectParser()); } public static ParamSpec extractParamField(Field f) { return parse(f, SpecParsers.getParamFieldParser()); } public static ParamSpec[] extractParamParamter(Method m) { return parse(m, SpecParsers.getParamParamterParser()); } protected static R parse(T t, Function parser) { return parser.apply(t); } protected static R parse(T t, U u, BiFunction parser) { return parser.apply(t, u); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/BeanAssemblerBootWatcher.java ================================================ package org.yx.bean; import java.util.List; import java.util.function.Predicate; import org.yx.annotation.spec.BeanSpec; import org.yx.annotation.spec.Specs; import org.yx.common.util.kit.PriorityKits; import org.yx.log.Logs; public class BeanAssemblerBootWatcher extends ParallelBootWatcher { @Override protected void handle(Class clz) throws Exception { BeanSpec b = Specs.extractBean(clz); BeanRegistry.registerClass(clz, b); } protected List>> priorityList(List> sortedClasses) { return PriorityKits.split(sortedClasses); } @Override public List> publish(List> sortedClasses, Predicate optional) throws Exception { List>> lists = priorityList(sortedClasses); this.serialPublish(lists.get(0), optional); Logs.ioc().trace("handled lower bean classes:{}", lists.get(0)); super.publish(lists.get(1), optional); this.serialPublish(lists.get(2), optional); Logs.ioc().trace("handled higher bean classes:{}", lists.get(2)); Logs.ioc().debug("full beans:{}", InnerIOC.beans()); return null; } @Override public int order() { return 1000; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/BeanFieldFinder.java ================================================ package org.yx.bean; import java.lang.reflect.Field; import org.yx.annotation.spec.InjectSpec; /** * 寻找bean里面需要注入的字段的对象。 */ public interface BeanFieldFinder { /** * 查找要注入该字段的对象 * * @param f bean中待注入的field * @param bean 字段所在的bean * @param inject 注入属性 * @return 将要被注入该field的对象,可能是单个对象,也可能是数组或List。查找不到就返回null * @throws Exception 如果抛出异常,就表示启动失败 */ Object findTarget(Field f, Object bean, InjectSpec inject) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/BeanKit.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Set; import org.yx.util.Loader; import org.yx.util.StringUtil; public final class BeanKit { public static Class getTargetClass(Object bean) { return bean instanceof Boxed ? ((Boxed) bean).targetRawClass() : bean.getClass(); } public static String resloveBeanName(Class clz) { String name = StringUtil.uncapitalize(clz.getSimpleName()); if (name.endsWith("Impl")) { name = name.substring(0, name.length() - 4); } return name; } public static Set resloveBeanNames(Class clazz) { Set> interfaces = new HashSet<>(); resloveSuperClassAndInterface(clazz, interfaces); Set names = new HashSet<>(); for (Class clz : interfaces) { names.add(resloveBeanName(clz)); } return names; } private static void resloveSuperClassAndInterface(Class clazz, Set> interfaces) { while (clazz != null && !clazz.getName().startsWith(Loader.JAVA_PRE) && (clazz.getModifiers() & Modifier.PUBLIC) != 0) { interfaces.add(clazz); Class[] ifcs = clazz.getInterfaces(); if (ifcs != null) { for (Class ifc : ifcs) { resloveSuperClassAndInterface(ifc, interfaces); } } clazz = clazz.getSuperclass(); } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/BeanPool.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.yx.bean.aop.AopContext; import org.yx.bean.aop.AopExecutor; import org.yx.bean.aop.AopExecutorManager; import org.yx.bean.aop.asm.AsmUtils; import org.yx.bean.aop.asm.ProxyClassVistor; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.log.Logs; import org.yx.util.Loader; import org.yx.util.StringUtil; public final class BeanPool { private final ConcurrentMap slotMap = new ConcurrentHashMap<>(128); private Class proxyIfNeed(Class clz) throws Exception { Method[] methods = clz.getDeclaredMethods(); Map aopMethods = new HashMap<>(); AopExecutorManager manager = AopExecutorManager.get(); for (Method m : methods) { int modifier = m.getModifiers(); if (!AsmUtils.canProxy(modifier)) { continue; } if (!Modifier.isPublic(modifier) && !Modifier.isProtected(modifier)) { continue; } List list = manager.willProxyExcutorSuppliers(clz, m); if (list.isEmpty()) { continue; } Class returnType = m.getReturnType(); if (returnType != null && returnType != Void.TYPE && returnType.isPrimitive()) { Logs.aop().warn("{}.{}()的返回值是{},这个方法aop无法屏蔽异常!", clz.getName(), m.getName(), returnType); } int index = manager.indexSupplier(list); aopMethods.put(m, index); if (Logs.aop().isDebugEnabled()) { List aopList = new ArrayList<>(list.size()); for (AopContext c : list) { aopList.add(c.getAopExecutor()); } Logs.aop().debug("{}.{}被{}代理了,序号:{}", clz.getName(), m.getName(), aopList, index); } } if (aopMethods.isEmpty()) { return clz; } ClassReader cr = new ClassReader(AsmUtils.openStreamForClass(clz)); ClassWriter cw = new ClassWriter(cr, AppInfo.getInt("sumk.asm.writer.box.compute", ClassWriter.COMPUTE_MAXS)); String newClzName = AsmUtils.proxyCalssName(clz); ProxyClassVistor cv = new ProxyClassVistor(cw, newClzName, clz, aopMethods); cr.accept(cv, AsmUtils.asmVersion()); return AsmUtils.defineClass(newClzName, cw.toByteArray(), clz.getClassLoader()); } public List beanNames() { Set names = slotMap.keySet(); return new ArrayList<>(names); } public Collection beans() { Map beans = new IdentityHashMap<>(slotMap.size()); for (NameSlot v : slotMap.values()) { for (Object bean : v.beans()) { beans.putIfAbsent(bean, Boolean.TRUE); } } return beans.keySet(); } public T putClass(String beanName, Class clz) throws Exception { Objects.requireNonNull(clz); Collection names = (beanName == null || (beanName = beanName.trim()).isEmpty()) ? BeanKit.resloveBeanNames(clz) : StringUtil.splitAndTrim(beanName, Const.COMMA, Const.SEMICOLON); if (names == null || names.isEmpty()) { names = Collections.singleton(BeanKit.resloveBeanName(clz)); } Class proxyClz = this.proxyIfNeed(clz); Object bean = Loader.newInstance(proxyClz); for (String name : names) { put(name, bean); } return this.getBean(beanName, clz); } public T putBean(String beanName, T bean) { Class clz = Objects.requireNonNull(bean).getClass(); Collection names = (beanName == null || (beanName = beanName.trim()).isEmpty()) ? BeanKit.resloveBeanNames(clz) : StringUtil.splitAndTrim(beanName, Const.COMMA, Const.SEMICOLON); if (names == null || names.isEmpty()) { names = Collections.singleton(BeanKit.resloveBeanName(clz)); } for (String name : names) { put(name, bean); } return bean; } private synchronized boolean put(String name, Object bean) { Logs.ioc().debug("add bean {} : {}", name, bean); NameSlot oldSlot = slotMap.putIfAbsent(name, new NameSlot(name, new Object[] { bean })); if (oldSlot == null) { return true; } return oldSlot.appendBean(bean); } public T getBean(String name, Class clz) { if (name == null || name.length() == 0) { name = BeanKit.resloveBeanName(clz); } NameSlot bw = slotMap.get(name); return bw == null ? null : bw.getBean(clz); } public List getBeans(String name, Class clz) { if (name == null || name.length() == 0) { name = BeanKit.resloveBeanName(clz); } NameSlot slot = slotMap.get(name); return slot == null ? Collections.emptyList() : slot.getBeans(clz); } public NameSlot getSlot(String name) { return slotMap.get(name); } public void clear() { slotMap.clear(); } @Override public String toString() { return slotMap.toString(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/BeanProvider.java ================================================ package org.yx.bean; import java.util.List; public interface BeanProvider { List getBeans(String name, Class clz); List beanNames(); T getBean(String name, Class clz); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/BeanRegistry.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.Collection; import org.yx.annotation.spec.BeanSpec; import org.yx.base.context.AppContext; import org.yx.common.Predicator; import org.yx.log.Logs; import org.yx.util.Loader; import org.yx.util.StringUtil; public class BeanRegistry { public static void registerClass(Class clz, BeanSpec spec) throws Exception { if (spec == null || !valid(clz, spec)) { return; } if (FactoryBean.class.isAssignableFrom(clz)) { FactoryBean factory = (FactoryBean) Loader.newInstance(clz); registerBeans(factory.beans()); } else { InnerIOC.putClass(spec.value(), clz); } } public static boolean valid(Class clz, BeanSpec bean) { String v = bean.conditionOnProperty(); if (StringUtil.isNotEmpty(v)) { boolean match = bean.onProperty(); boolean exist = Predicator.test(v, name -> AppContext.inst().getAppInfo(name, null) != null); if (match ^ exist) { Logs.system().info("{} exclude because conditionOnProperty donot meet.on match:{},exist:{}", clz.getName(), match, exist); return false; } } return true; } public static void registerBeans(Collection beans) throws Exception { if (beans != null && beans.size() > 0) { for (Object obj : beans) { registerBean(null, obj); } } } public static void registerBean(String name, Object obj) throws Exception { if (obj == null) { return; } Class clz = obj.getClass(); if (clz == Class.class) { InnerIOC.putClass(name, (Class) obj); return; } if (clz == NamedBean.class) { NamedBean named = (NamedBean) obj; registerBean(named.getBeanName(), named.getBean()); return; } if (clz == InterfaceBean.class) { InterfaceBean complex = (InterfaceBean) obj; registerBean(BeanKit.resloveBeanName(complex.getIntf()), complex.getBean()); return; } InnerIOC.putBean(name, obj); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/Booter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.function.Predicate; import org.slf4j.Logger; import org.yx.annotation.Bean; import org.yx.annotation.spec.InjectSpec; import org.yx.annotation.spec.Specs; import org.yx.base.context.AppContext; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.base.scaner.ClassScaner; import org.yx.bean.aop.AopExecutorManager; import org.yx.bean.aop.AopExecutorSupplier; import org.yx.bean.aop.asm.AsmUtils; import org.yx.bean.watcher.BeanCreateWatcher; import org.yx.bean.watcher.BeanInjectWatcher; import org.yx.bean.watcher.BootWatcher; import org.yx.common.util.kit.PriorityKits; import org.yx.conf.Const; import org.yx.exception.SimpleSumkException; import org.yx.log.Logs; import org.yx.main.SumkServer; import org.yx.util.CollectionUtil; import org.yx.util.Loader; import org.yx.util.StringUtil; public final class Booter { private final Logger logger = Logs.ioc(); private static final String FACTORIES_FILE = "META-INF/sumk.factories"; private static final String SUMK_IOC_EXCLUDE = "sumk.ioc.exclude"; /** * 这里定义精确扫描的类,这些类也需要@Bean注解 */ private static final String SUMK_IOC_BEAN = "sumk.ioc.bean"; private static final String SUMK_IOC_BOOT_WATCHER = "sumk.ioc.boot.watcher"; /** * 符合这里表达式的类,即使加载出错,即使出了问题,也认为是正常的。 这里的加载出错指的是class文件能找到,但是它的关联类加载出错 */ private static final String SUMK_IOC_OPTIONAL = "sumk.ioc.optional"; public void start(List packageNames) throws Exception { onStart(packageNames); BeanScanerInstance definition = this.buildBeanScanerInstance(packageNames); List> orignClazzList = this.loadAndSortClasses(definition); List watchers = loadWatcher(definition); this.refresh(orignClazzList, watchers, definition); this.autoWiredAll(definition); new PluginBooter().start(); Runtime.getRuntime().addShutdownHook(new Thread(SumkServer::destroy)); } private BeanScanerInstance buildBeanScanerInstance(List packageNames) throws IOException { FactoriesInstance fi = this.parseFactoriesInstance(); BeanScanerInstance definition = new BeanScanerInstance(packageNames, fi); logger.debug("finally bean scan instance:{}", definition); return definition; } private void onStart(List packageNames) { AopExecutorManager.reset(); InnerIOC.clear(); AsmUtils.clearProxyClassLoaders(); if (packageNames.isEmpty()) { logger.warn("property [sumk.ioc] is empty"); return; } logger.debug("start ioc with packages:[{}]", packageNames); } /** * 包含了从配置以及jar中的meta文件加载的 * * @return * @throws IOException */ private FactoriesInstance parseFactoriesInstance() throws IOException { FactoriesInstance ret = FactoriesInstance.fromContext(); Enumeration urls = Loader.getResources(FACTORIES_FILE); while (urls.hasMoreElements()) { URL url = urls.nextElement(); try (InputStream is = url.openStream()) { Properties properties = new Properties(); properties.load(is); FactoriesInstance f = FactoriesInstance.fromProperties(properties); logger.debug("factories [{}]: {}", url, f); ret.addAll(f); } } return ret; } private List> unmodifyClassList(List> list) { return CollectionUtil.unmodifyList(list.toArray(new Class[list.size()])); } private void refresh(List> clazzList, List watchers, BeanScanerInstance definition) throws Exception { clazzList = unmodifyClassList(clazzList); watchers.sort(null); for (BootWatcher b : watchers) { List> temp = b.publish(clazzList, definition.optionalMatcher); if (temp != null) { clazzList = unmodifyClassList(temp); } } } private List> loadAndSortClasses(BeanScanerInstance definition) throws Exception { Collection clzsFromPackage = ClassScaner.listClasses(definition.packageNames); Set clzs = new HashSet<>(clzsFromPackage); if (CollectionUtil.isNotEmpty(definition.beans)) { clzs.addAll(definition.beans); } List> clazzList = new ArrayList<>(clzs.size()); for (String c : clzs) { Class clz = this.loadClass(c, definition); if (clz == null) { continue; } if (AopExecutorSupplier.class.isAssignableFrom(clz) && clz.isAnnotationPresent(Bean.class)) { InnerIOC.putBean(null, Loader.newInstance(clz)); continue; } clazzList.add(clz); } List> sortedClazzList = PriorityKits.sort(clazzList); this.printClassListDebugInfo(sortedClazzList); return sortedClazzList; } private void printClassListDebugInfo(List> sortedClazzList) { if (!logger.isDebugEnabled()) { return; } logger.debug("scan class size:{}, {} {}..{} {}", sortedClazzList.size(), sortedClazzList.get(0).getSimpleName(), sortedClazzList.get(1).getSimpleName(), sortedClazzList.get(sortedClazzList.size() - 2).getSimpleName(), sortedClazzList.get(sortedClazzList.size() - 1).getSimpleName()); logger.trace("ordered class:\n{}", sortedClazzList); } private List loadWatcher(BeanScanerInstance definition) throws Exception { Set ws = definition.watchers; if (CollectionUtil.isEmpty(ws)) { return Collections.emptyList(); } List watchers = new ArrayList<>(); for (String w : ws) { Class bw = this.loadClass(w, definition); if (bw == null) { continue; } if (!BootWatcher.class.isAssignableFrom(bw)) { logger.error("[{}] must implement BootWatcher", bw); AppContext.startFailed(); continue; } watchers.add((BootWatcher) Loader.newInstance(bw)); } return watchers; } private Class loadClass(String fullName, BeanScanerInstance definition) { if (definition.excludeMatcher.test(fullName)) { logger.info("[{}] ingored by [{}]", fullName, SUMK_IOC_EXCLUDE); return null; } try { if (logger.isTraceEnabled()) { logger.trace("{} begin loading", fullName); } Class clz = Loader.loadClass(fullName); if ((clz.getModifiers() & (Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.PUBLIC | Modifier.INTERFACE)) != Modifier.PUBLIC || clz.isAnonymousClass() || clz.isLocalClass() || clz.isAnnotation() || clz.isEnum()) { logger.trace("{} ingored because it is not normal public class", fullName); return null; } return clz; } catch (Throwable e) { if ((e instanceof LinkageError) && definition.optionalMatcher.test(fullName)) { logger.debug("{} ignored when load because: [{}]", fullName, e); return null; } logger.error(fullName + "加载失败", e); AppContext.startFailed(); throw new RuntimeException(e); } } private void injectField(Field f, Object bean, Object target) throws IllegalAccessException { if (!f.isAccessible()) { f.setAccessible(true); } f.set(bean, target); } private void autoWiredAll(BeanScanerInstance definition) throws Exception { List beans = CollectionUtil.unmodifyList(InnerIOC.beans().toArray()); logger.trace("after beans create..."); for (BeanCreateWatcher w : IOC.getBeans(BeanCreateWatcher.class)) { w.afterCreate(beans); } logger.trace("inject beans properties..."); beans = CollectionUtil.unmodifyList(InnerIOC.beans().toArray()); for (Object bean : beans) { injectProperties(bean, definition); } logger.trace("after beans installed..."); for (BeanInjectWatcher watcher : IOC.getBeans(BeanInjectWatcher.class)) { watcher.afterInject(beans); } logger.trace("plugins starting..."); } private void injectProperties(Object bean, BeanScanerInstance definition) throws Exception { Class tempClz = bean.getClass(); while (tempClz != null && (!tempClz.getName().startsWith(Loader.JAVA_PRE))) { Field[] fs = tempClz.getDeclaredFields(); for (Field f : fs) { InjectSpec inject = Specs.extractInject(bean, f); if (inject == null) { continue; } Object target = definition.fieldFinder.findTarget(f, bean, inject); if (target == null) { if (inject.allowEmpty()) { continue; } throw new SimpleSumkException(235435658, bean.getClass().getName() + "." + f.getName() + " cannot injected. because bean not founds"); } injectField(f, bean, target); } tempClz = tempClz.getSuperclass(); } } private static final class FactoriesInstance { final Set beans = new HashSet<>(); final Set bootWatchers = new HashSet<>(); final Set excludes = new HashSet<>(); final Set optionals = new HashSet<>(); private static void addValues(Set set, String factoryNamesProperty) { if (StringUtil.isEmpty(factoryNamesProperty)) { return; } List terms = StringUtil.splitAndTrim(factoryNamesProperty, Const.COMMA); set.addAll(terms); } public void addAll(FactoriesInstance f) { this.beans.addAll(f.beans); this.bootWatchers.addAll(f.bootWatchers); this.excludes.addAll(f.excludes); this.optionals.addAll(f.optionals); } public static FactoriesInstance fromProperties(Properties properties) { FactoriesInstance fi = new FactoriesInstance(); addValues(fi.beans, properties.getProperty(SUMK_IOC_BEAN)); addValues(fi.bootWatchers, properties.getProperty(SUMK_IOC_BOOT_WATCHER)); addValues(fi.excludes, properties.getProperty(SUMK_IOC_EXCLUDE)); addValues(fi.optionals, properties.getProperty(SUMK_IOC_OPTIONAL)); return fi; } public static FactoriesInstance fromContext() { FactoriesInstance fi = new FactoriesInstance(); addValues(fi.beans, AppContext.inst().getAppInfo(SUMK_IOC_BEAN, "")); addValues(fi.bootWatchers, AppContext.inst().getAppInfo(SUMK_IOC_BOOT_WATCHER, "")); addValues(fi.excludes, AppContext.inst().getAppInfo(SUMK_IOC_EXCLUDE, "")); addValues(fi.optionals, AppContext.inst().getAppInfo(SUMK_IOC_OPTIONAL, "")); return fi; } @Override public String toString() { return "FactoriesInstance [beans=" + beans + ", bootWatchers=" + bootWatchers + ", excludes=" + excludes + ", optionals=" + optionals + "]"; } } private static final class BeanScanerInstance { final BeanFieldFinder fieldFinder = AppContext.inst().get(BeanFieldFinder.class, new DefaultBeanFieldFinder()); final List packageNames; final Set beans; final Set watchers; final Predicate excludeMatcher; final Predicate optionalMatcher; public BeanScanerInstance(List packageNames, FactoriesInstance fi) { this.packageNames = packageNames; this.beans = fi.beans; this.watchers = fi.bootWatchers; this.excludeMatcher = createWildcardMatcher(fi.excludes); this.optionalMatcher = createWildcardMatcher(fi.optionals); } private Predicate createWildcardMatcher(Collection patterns) { if (CollectionUtil.isEmpty(patterns)) { return BooleanMatcher.FALSE; } StringBuilder sb = new StringBuilder(); for (String v : patterns) { sb.append(v).append(Matchers.SPLIT); } return Matchers.createWildcardMatcher(sb.toString(), 2); } @Override public String toString() { return "BeanScanerInstance [packageNames=" + packageNames + ", excludeMatcher=" + excludeMatcher + ", optionalMatcher=" + optionalMatcher + ", beans=" + beans + ", watchers=" + watchers + "]"; } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/Boxed.java ================================================ package org.yx.bean; public interface Boxed { Class targetRawClass(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/ComplexBean.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; public interface ComplexBean { } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/DefaultBeanFieldFinder.java ================================================ package org.yx.bean; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; import java.util.List; import org.yx.annotation.spec.InjectSpec; import org.yx.exception.SimpleSumkException; import org.yx.util.CollectionUtil; import org.yx.util.Loader; public class DefaultBeanFieldFinder implements BeanFieldFinder { @Override public Object findTarget(Field f, Object bean, InjectSpec inject) throws Exception { Class fieldType = f.getType(); if (fieldType.isArray()) { return getArrayField(f, bean, inject.allowEmpty()); } if (List.class == fieldType || Collection.class == fieldType) { return getListField(f, bean, inject.allowEmpty()); } return getSimpleBean(f, inject); } protected Object getSimpleBean(Field f, InjectSpec inject) { Class clz = f.getType(); String name = inject.value(); if (name != null && (name = name.trim()).length() > 0) { return IOC.get(name, clz); } List list = IOC.getBeans(f.getName(), clz); if (list.size() == 1) { return list.get(0); } if (list.size() > 1) { for (Object obj : list) { if (clz == BeanKit.getTargetClass(obj)) { return obj; } } if (inject.allowMulti()) { return list.get(0); } } if (inject.allowMulti()) { list = IOC.getBeans(null, clz); if (list.size() > 0) { return list.get(0); } } return IOC.get(clz); } protected List getListField(Field f, Object bean, boolean allowEmpty) throws ClassNotFoundException { String genericName = f.getGenericType().getTypeName(); if (genericName == null || genericName.isEmpty() || !genericName.contains("<")) { throw new SimpleSumkException(239845611, bean.getClass().getName() + "." + f.getName() + "is List,but not List"); } genericName = genericName.substring(genericName.indexOf("<") + 1, genericName.length() - 1); Class clz = Loader.loadClass(genericName); if (clz == Object.class) { throw new SimpleSumkException(23984568, bean.getClass().getName() + "." + f.getName() + ": beanClz of @Inject in list type cannot be null"); } List target = IOC.getBeans(clz); if (target == null || target.isEmpty()) { if (!allowEmpty) { throw new SimpleSumkException(235435652, bean.getClass().getName() + "." + f.getName() + " is empty."); } return Collections.emptyList(); } return CollectionUtil.unmodifyList(target.toArray()); } protected Object[] getArrayField(Field f, Object bean, boolean allowEmpty) { Class clz = f.getType().getComponentType(); List target = IOC.getBeans(clz); if (target == null || target.isEmpty()) { if (!allowEmpty) { throw new SimpleSumkException(235435651, bean.getClass().getName() + "." + f.getName() + " is empty."); } return (Object[]) Array.newInstance(clz, 0); } return target.toArray((Object[]) Array.newInstance(clz, target.size())); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/FactoryBean.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.Collection; /** * 这个类可以被exclude过滤,但是beans()返回的bean不受exclude过滤 */ public interface FactoryBean { /** * 返回的列表中的元素,可以是class类、NamedBean或实例化后的对象。Collection、Map类型的Object,不会对它做额外处理。
* 如果是实例化后的对象,该对象中sumk相关的注解(@Web、@Bean、@Soa等)会被忽略
* 无论是对象还是class,这里的bean中的@Inject能被正确注入 注意:不要出现类名重复的对象 * * @return 可以为null或空list */ Collection beans(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/IOC.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.List; import java.util.Objects; import org.yx.base.Ordered; import org.yx.exception.SumkException; import org.yx.log.Logs; public final class IOC { private static BeanProvider provider = new InnerProvider(); public static void setProvider(BeanProvider provider) { IOC.provider = Objects.requireNonNull(provider); } /** * 获取对应的bean * * @param 返回值类型 * @param name bean的名称 * @return 如果不存在,就返回null。如果bean不止一个,会抛出SumkException异常 */ public static T get(String name) { return get(name, null); } public static T get(Class clz) { return get(null, clz); } public static T get(String name, Class clz) { return provider.getBean(name, clz); } public static List getBeans(Class clz) { return provider.getBeans(null, clz); } /** * 获取该类型bean的第一个,如果不存在就抛出异常。 * * @param bean的类型 * @param clz bean的类型,返回order最小的那个 * @param allowEmpty true表示允许为空,否则会抛出异常 * @return 返回第一个符合条件的bean。被自定义名称的bean可能获取不到 */ public static T getFirstBean(Class clz, boolean allowEmpty) { List factorys = getBeans(clz); if (factorys.isEmpty()) { if (allowEmpty) { return null; } throw new SumkException(353451, clz.getName() + "没有实现类,或者实现类不是Bean"); } T f = factorys.get(0); Logs.ioc().debug("{}第一个bean是{}", clz.getName(), f); return f; } public static List getBeans(String name, Class clz) { return provider.getBeans(name, clz); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/InnerIOC.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.Collection; import java.util.List; import org.yx.bean.aop.AopExecutorChain; import org.yx.bean.aop.AopExecutorManager; public final class InnerIOC { private static final BeanPool pool = new BeanPool(); static BeanPool pool() { return pool; } public static T putClass(String name, Class clz) throws Exception { return pool.putClass(name, clz); } public static T putClassByInterface(Class intf, Class clz) throws Exception { return pool.putClass(BeanKit.resloveBeanName(intf), clz); } public static T putBean(String beanName, T bean) { return pool.putBean(beanName, bean); } public static T getOrCreate(Class clz) throws Exception { T obj = IOC.get(clz); if (obj != null) { return obj; } return putClass(null, clz); } public static List beanNames() { return pool.beanNames(); } public static NameSlot getSlot(String name) { return pool.getSlot(name); } public static Collection beans() { return pool.beans(); } public static void clear() { pool.clear(); } public static String info() { return pool.toString(); } public static AopExecutorChain getAopExecutorChain(int index) { return AopExecutorManager.get().getChain(index); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/InnerProvider.java ================================================ package org.yx.bean; import java.util.List; public class InnerProvider implements BeanProvider { @Override public List getBeans(String name, Class clz) { return InnerIOC.pool().getBeans(name, clz); } @Override public List beanNames() { return InnerIOC.beanNames(); } @Override public T getBean(String name, Class clz) { return InnerIOC.pool().getBean(name, clz); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/InterfaceBean.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.Objects; import org.yx.exception.SimpleSumkException; public final class InterfaceBean implements ComplexBean { private final Class intf; private final Object bean; public InterfaceBean(Class intf, Object bean) { this.intf = Objects.requireNonNull(intf); if (bean instanceof ComplexBean) { throw new SimpleSumkException(233654645, "bean can not be a ComplexBean object"); } this.bean = Objects.requireNonNull(bean); } public Class getIntf() { return intf; } public Object getBean() { return bean; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/NameSlot.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.ArrayList; import java.util.List; import org.yx.exception.SumkException; import org.yx.exception.SumkExceptionCode; import org.yx.log.Logs; import org.yx.util.CollectionUtil; public class NameSlot { private final String name; private Object[] beans; public NameSlot(String name, Object[] beans) { this.name = name; this.beans = beans == null ? new Object[0] : beans; } public List beans() { return CollectionUtil.unmodifyList(beans); } public synchronized boolean appendBean(Object bean) { Object[] beans = this.beans; if (beans.length == 0) { this.beans = new Object[] { bean }; return true; } Class clz = BeanKit.getTargetClass(bean); for (Object o : beans) { if (clz == BeanKit.getTargetClass(o)) { Logs.ioc().warn("{}={} duplicate,will be ignored.", name, clz.getName()); return false; } } Object[] newBeans = new Object[beans.length + 1]; System.arraycopy(beans, 0, newBeans, 0, beans.length); newBeans[newBeans.length - 1] = bean; this.beans = newBeans; return true; } private Class resolveType(Class clz) { if (clz == null) { return Object.class; } if (Boxed.class.isAssignableFrom(clz)) { return clz.getSuperclass(); } return clz; } @SuppressWarnings("unchecked") public T getBean(Class clz) { clz = (Class) this.resolveType(clz); List ret = new ArrayList<>(2); for (Object b : this.beans) { Class targetClz = BeanKit.getTargetClass(b); if (targetClz == clz) { return (T) b; } if (clz.isAssignableFrom(targetClz)) { ret.add(b); } } if (ret.isEmpty()) { return null; } if (ret.size() > 1) { throw new SumkException(SumkExceptionCode.TOO_MANY_BEAN, name + "存在多个" + clz.getName() + "实例"); } return (T) ret.get(0); } @SuppressWarnings("unchecked") public List getBeans(Class clz) { clz = (Class) this.resolveType(clz); List list = new ArrayList<>(this.beans.length); for (Object o : this.beans) { if (clz.isInstance(o)) { list.add((T) o); } } if (list.size() > 1 && Comparable.class.isAssignableFrom(clz)) { list.sort(null); } return list; } public String name() { return name; } @Override public String toString() { StringBuilder sb = new StringBuilder().append(name).append(" :"); for (Object o : this.beans) { sb.append(" ").append(BeanKit.getTargetClass(o).getName()); } return sb.toString(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/NamedBean.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.Objects; import org.yx.exception.SimpleSumkException; public final class NamedBean implements ComplexBean { private final String beanName; /** * 可以是class类,也可以包含实力化后的对象。但不能是NamedBean */ private final Object bean; /** * @param beanName bean的名称,不能为null或空串 * @param bean 可以是class类,也可以包含实力化后的对象。但不能是NamedBean */ public NamedBean(String beanName, Object bean) { beanName = Objects.requireNonNull(beanName, "beanName cannot be null").trim(); if (beanName.isEmpty()) { throw new SimpleSumkException(233654645, "bean name can not be empty"); } if (bean instanceof ComplexBean) { throw new SimpleSumkException(233654645, "bean can not be a ComplexBean object"); } this.beanName = beanName; this.bean = Objects.requireNonNull(bean); } public String getBeanName() { return beanName; } public Object getBean() { return bean; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/ParallelBootWatcher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; import org.yx.bean.watcher.BootWatcher; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.main.SumkServer; public abstract class ParallelBootWatcher implements BootWatcher { /** * ioc启动处理 * * @param clz 这个clz是原始的类 * @throws Exception 处理时发生的异常 */ protected abstract void handle(Class clz) throws Exception; @Override public List> publish(List> sortedClasses, Predicate optional) throws Exception { ExecutorService executor = createExecutor(); try { parallelPublish(sortedClasses, optional, executor); } finally { executor.shutdown(); } return null; } protected void serialPublish(List> sortedClasses, Predicate optional) throws Exception { for (Class clz : sortedClasses) { BootCallable c = new BootCallable(clz, optional, this); c.call(); } } protected void parallelPublish(List> scanedClasses, Predicate optional, ExecutorService executor) throws InterruptedException, ExecutionException, TimeoutException { List> futures = new ArrayList<>(scanedClasses.size()); for (Class clz : scanedClasses) { BootCallable c = new BootCallable(clz, optional, this); Future f = executor.submit(c); futures.add(f); } for (Future f : futures) { f.get(SumkServer.startTimeout(), TimeUnit.MILLISECONDS); } } protected ExecutorService createExecutor() { int poolSize = AppInfo.getInt("sumk.ioc.booter.threads", 4); if (poolSize < 1) { poolSize = 1; } ThreadPoolExecutor excutor = new ThreadPoolExecutor(poolSize, poolSize, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue()); excutor.allowCoreThreadTimeOut(true); return excutor; } private static class BootCallable implements Callable { private final Class clz; private final Predicate optional; private final ParallelBootWatcher watcher; public BootCallable(Class clz, Predicate optional, ParallelBootWatcher watcher) { this.clz = clz; this.optional = optional; this.watcher = watcher; } @Override public Boolean call() throws Exception { try { watcher.handle(clz); } catch (Throwable e) { String c = clz.getName(); if ((e instanceof LinkageError || e instanceof ClassNotFoundException) && (c.startsWith("org.yx.") || optional.test(c))) { Logs.ioc().debug("{} ignored in {} publish because: [{}]", c, watcher.getClass().getName(), e); return Boolean.TRUE; } Logs.ioc().error(c + " 在 " + watcher.getClass().getName() + "中执行失败", e); throw e; } return Boolean.TRUE; } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/Plugin.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import org.yx.base.Ordered; /** * 实现该接口,并且添加@Bean注解。会在sumk启动或停止的时候,被调用
* 这个接口一般用于mongo初始化等。但不用于修改IOC的内部结构。 * * @author 游夏 * */ public interface Plugin extends Ordered { /** * 在startAsync()之前执行,同步执行。如果抛出异常,会导致应用启动失败 */ default void prepare() { } /** * 在所有的prepare()之后异步执行。 如果抛出异常,会导致应用启动失败 */ void startAsync(); /** * 在所有的startAsync()之后执行。如果抛出异常,会导致应用启动失败 */ default void afterStarted() { } default void stop() { } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/PluginBooter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.yx.base.context.AppContext; import org.yx.base.thread.SumkExecutorService; import org.yx.common.validate.Validators; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.main.SumkServer; import org.yx.util.SumkThreadPool; public class PluginBooter { public void start() { Validators.init(); startBeans(); } private void startBeans() { List plugins = IOC.getBeans(Plugin.class); if (plugins == null || plugins.isEmpty()) { return; } for (Plugin plugin : plugins) { plugin.prepare(); } CountDownLatch latch = new CountDownLatch(plugins.size()); SumkExecutorService executor = SumkThreadPool.executor(); for (Plugin plugin : plugins) { executor.execute(() -> { try { plugin.startAsync(); latch.countDown(); Logs.ioc().debug("{} startAsync finished", plugin.getClass().getSimpleName()); } catch (Throwable e) { Logs.ioc().error(plugin.getClass().getSimpleName() + " start failed", e); AppContext.startFailed(); } }); } preHotCoreThreads(executor); long timeout = SumkServer.startTimeout(); try { if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { Logs.ioc().error("plugin无法在{}ms内全部启动,导致整个项目启动超时", timeout); AppContext.startFailed(); } } catch (InterruptedException e) { Logs.ioc().error("receive InterruptedException in plugin starting"); Thread.currentThread().interrupt(); AppContext.startFailed(); } for (Plugin plugin : plugins) { plugin.afterStarted(); } Logs.ioc().debug("all plugin started"); } private void preHotCoreThreads(SumkExecutorService executor) { if (SumkServer.isRpcEnable() || SumkServer.isHttpEnable()) { SumkThreadPool.executor().setCorePoolSize(200); } if (AppInfo.getBoolean("sumk.thread.prestartAllCoreThreads", true) && (SumkServer.isHttpEnable() || SumkServer.isRpcEnable()) && executor instanceof ThreadPoolExecutor) { ThreadPoolExecutor pool = (ThreadPoolExecutor) executor; pool.prestartAllCoreThreads(); } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/AopContext.java ================================================ package org.yx.bean.aop; import java.util.Objects; public class AopContext { private final AopExecutorSupplier supplier; private final Object attach; public AopContext(AopExecutorSupplier supplier, Object attach) { this.supplier = Objects.requireNonNull(supplier); this.attach = attach; } public AopExecutor getAopExecutor() { return this.supplier.get(attach); } public AopExecutorSupplier getSupplier() { return supplier; } public Object getAttach() { return attach; } @Override public int hashCode() { return Objects.hash(attach, supplier); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AopContext other = (AopContext) obj; return Objects.equals(attach, other.attach) && Objects.equals(supplier, other.supplier); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/AopExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop; /** * 返回值如果是原始类型,因为不支持null,所以在before()返回false和after()吃掉异常的情况下,框架会抛出异常。 执行顺序: *
    *
  1. 先执行before(),如果返回false,就直接执行close()
  2. *
  3. 如果before()和业务方法都执行成功,就执行after()
  4. *
  5. 有异常发生就执行onError(),这个是用来转换异常,如果异常被前面的Executor转换了,将不再被执行。这个方法不建议抛出异常
  6. *
  7. close()无论什么情况都会被执行。这个方法不建议抛出异常
  8. *
* * @author youtl * */ public interface AopExecutor { /** * 这个方法可以抛出异常,如果抛出异常,就不会执行后面的AopExecutor,并且真正的业务方法也不会被执行你 * * @param params 原始方法的参数 * @throws Exception 如果抛出异常,会中断后面的执行 */ void before(Object[] params) throws Exception; /** * 无论上面的before()方法执行得怎么样,所有调用过before()的AopExecutor, 它的close()都会确保被调用到 * * @param result 原始方法的返回值。返回值引用不可变,但可以修改返回值的内部属性 * @param e 原始或上一个AopExecutor的异常,如果没有发生异常,它就是null * @param methodExecuted true表示业务方法有被执行 * @return 原始异常或者处理后的异常。如果返回null,就表示没有异常了 */ Throwable after(Object result, Throwable e, boolean methodExecuted); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/AopExecutorChain.java ================================================ package org.yx.bean.aop; import org.yx.log.Logs; import org.yx.util.ExceptionUtil; /** * 嵌套式执行,先执行before()的晚执行after() * * @author youtl * */ public class AopExecutorChain { private final AopExecutor[] executors; private int lastExecutor; public AopExecutorChain(AopExecutor[] excutors) { this.executors = excutors; } public void before(Object[] params) throws Exception { for (int i = 0; i < executors.length; i++) { this.lastExecutor = i; AopExecutor excutor = executors[lastExecutor]; excutor.before(params); } } public void after(Object result, Throwable e, boolean methodExecuted) { Throwable ex = e; for (int i = lastExecutor; i >= 0; i--) { try { ex = executors[i].after(result, ex, methodExecuted); } catch (Throwable e2) { Logs.aop().error("excpetion raised when after() in aopExecutor " + executors[i], e2); ex = e2; } } if (ex != null) { Logs.aop().error(ex.getMessage(), ex); throw ExceptionUtil.toRuntimeException(ex); } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/AopExecutorManager.java ================================================ package org.yx.bean.aop; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.yx.bean.IOC; public class AopExecutorManager { private static AopExecutorManager INSTANCE = new AopExecutorManager(); public static AopExecutorManager get() { return INSTANCE; } public static void reset() { INSTANCE = new AopExecutorManager(); } private List aopContexts = new ArrayList<>(); public AopExecutorChain getChain(int index) { AopContext[] aopContexts = this.aopContexts.get(index); AopExecutor[] excutors = new AopExecutor[aopContexts.length]; for (int i = 0; i < aopContexts.length; i++) { excutors[i] = aopContexts[i].getAopExecutor(); } return new AopExecutorChain(excutors); } public List willProxyExcutorSuppliers(Class clz, Method method) { List baseSuppliers = IOC.getBeans(AopExecutorSupplier.class); if (baseSuppliers.isEmpty()) { return Collections.emptyList(); } List list = new ArrayList<>(baseSuppliers.size()); for (AopExecutorSupplier supplier : baseSuppliers) { Object attach = supplier.willProxy(clz, method); if (attach != null) { list.add(new AopContext(supplier, attach)); } } return list; } public synchronized int indexSupplier(List supplierList) { AopContext[] supplier = supplierList.toArray(new AopContext[supplierList.size()]); int currentLength = this.aopContexts.size(); this.aopContexts.add(supplier); return currentLength; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/AopExecutorSupplier.java ================================================ package org.yx.bean.aop; import java.lang.reflect.Method; import org.yx.annotation.doc.NotNull; import org.yx.base.Ordered; /** *
    *
  • 实现类也需要@Bean注解,但是它的willProxy()不能调用其它的bean
  • *
  • 执行顺序由order()方法决定。
  • *
* */ public interface AopExecutorSupplier extends Ordered { /** * 判断是否满足代理条件,本方法只在IOC框架启动的时候调用,并且在bean初始化之前调用,所以里面不能使用其它的bean。 * 私有方法等不符合代理条件的方法,不会调用这个方法 * * @param clz 方法的原始类 * @param rawMethod 原始的方法 * @return null表示不需要代理,其它对象表示需要代理,最简单的办法就是返回rawMethod。该返回的对象,会传递给get()方法 */ Object willProxy(Class clz, Method rawMethod); /** * 一次aop方法执行,就会调用一次每个符合当前aop条件的AopExcutorSupplier。 返回的对象可以每次都不同,也可以是同一个。
* 注意本方法不能抛出异常,也不要开启资源占用,占用资源应该在before()里做 * * @param attach willProxy方法返回的对象 * @return aop的执行器,不能为null */ @NotNull AopExecutor get(Object attach); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/AsmUtils.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.Loader; public final class AsmUtils { private static final Map classLoaders = new ConcurrentHashMap<>(); public static String proxyCalssName(Class clz) { String name = clz.getName(); int index = name.lastIndexOf('.'); return name.substring(0, index) + ".sumkbox" + name.substring(index); } public static int asmVersion() { return AppInfo.getInt("sumk.asm.version", Opcodes.ASM7); } public static int jvmVersion() { return AppInfo.getInt("sumk.asm.jvm.version", Opcodes.V1_8); } public static InputStream openStreamForClass(Class clz) { String internalName = clz.getName().replace('.', '/') + ".class"; return Loader.getResourceAsStream(internalName); } public static boolean sameType(Type[] types, Class[] clazzes) { if (types.length != clazzes.length) { return false; } for (int i = 0; i < types.length; i++) { if (!Type.getType(clazzes[i]).equals(types[i])) { return false; } } return true; } public static List buildMethodInfos(List methods) throws IOException { Map, List> map = new HashMap<>(); for (Method m : methods) { List list = map.get(m.getDeclaringClass()); if (list == null) { list = new ArrayList<>(); map.put(m.getDeclaringClass(), list); } list.add(m); } List ret = new ArrayList<>(); for (List ms : map.values()) { ret.addAll(buildMethodInfos(ms.get(0).getDeclaringClass(), ms)); } return ret; } private static List buildMethodInfos(Class declaringClass, List methods) throws IOException { ClassReader cr = new ClassReader(openStreamForClass(declaringClass)); MethodInfoClassVisitor cv = new MethodInfoClassVisitor(methods); cr.accept(cv, 0); return cv.getMethodInfos(); } public static Method getMethod(Class clz, String methodName, Class[] paramTypes) { while (clz != Object.class) { Method[] ms = clz.getDeclaredMethods(); for (Method m : ms) { if (!m.getName().equals(methodName)) { continue; } Class[] paramTypes2 = m.getParameterTypes(); if (!Arrays.equals(paramTypes2, paramTypes)) { continue; } return m; } clz = clz.getSuperclass(); } return null; } private static ProxyClassLoader getProxyClassLoader(ClassLoader originClassLoader) { if (originClassLoader == null) { originClassLoader = Loader.loader(); } if (originClassLoader instanceof ProxyClassLoader) { return (ProxyClassLoader) originClassLoader; } return classLoaders.computeIfAbsent(originClassLoader, k -> new ProxyClassLoader(k)); } public static Class defineClass(String fullName, byte[] b, ClassLoader originClassLoader) throws Exception { String clzOutPath = AppInfo.get("sumk.asm.debug.output"); if (clzOutPath != null && clzOutPath.length() > 0) { try { File f = new File(clzOutPath, fullName + ".class"); try (FileOutputStream fos = new FileOutputStream(f)) { fos.write(b); fos.flush(); } } catch (Exception e) { if (Logs.asm().isTraceEnabled()) { Logs.asm().error(e.getLocalizedMessage(), e); } } } ProxyClassLoader classLoader = getProxyClassLoader(originClassLoader); synchronized (AsmUtils.class) { try { Class clz = classLoader.loadClass(fullName); Logs.asm().debug("{} was defined", fullName); return clz; } catch (Throwable e) { if (!(e instanceof ClassNotFoundException)) { Logs.asm().warn(fullName + " 加载失败", e); } } Class clz = classLoader.defineClass(fullName, b); if (clz == null) { throw new SumkException(235345436, "cannot load class " + fullName); } return clz; } } public static final int BADMODIFIERS = Modifier.ABSTRACT | Modifier.STATIC | Modifier.FINAL | Modifier.PRIVATE; public static boolean notPublicOnly(int modifiers) { return (modifiers & (Modifier.PUBLIC | BADMODIFIERS)) != Modifier.PUBLIC; } public static boolean canProxy(int modifiers) { return (modifiers & BADMODIFIERS) == 0; } public static List getImplicitFrame(String desc) { List locals = new ArrayList<>(5); if (desc.isEmpty()) { return locals; } int i = 0; while (desc.length() > i) { int j = i; switch (desc.charAt(i++)) { case 'Z': case 'C': case 'B': case 'S': case 'I': locals.add(Opcodes.INTEGER); break; case 'F': locals.add(Opcodes.FLOAT); break; case 'J': locals.add(Opcodes.LONG); break; case 'D': locals.add(Opcodes.DOUBLE); break; case '[': while (desc.charAt(i) == '[') { ++i; } if (desc.charAt(i) == 'L') { ++i; while (desc.charAt(i) != ';') { ++i; } } locals.add(desc.substring(j, ++i)); break; case 'L': while (desc.charAt(i) != ';') { ++i; } locals.add(desc.substring(j + 1, i++)); break; default: break; } } return locals; } public static Method getSameMethod(Method method, Class otherClass) { Class clz = method.getDeclaringClass(); if (clz == otherClass) { return method; } String methodName = method.getName(); Class[] argTypes = method.getParameterTypes(); Method[] proxyedMethods = otherClass.getMethods(); for (Method proxyedMethod : proxyedMethods) { if (proxyedMethod.getName().equals(methodName) && Arrays.equals(argTypes, proxyedMethod.getParameterTypes()) && !proxyedMethod.getDeclaringClass().isInterface()) { return proxyedMethod; } } return method; } public static void clearProxyClassLoaders() { classLoaders.clear(); } public static HashMap aopClassLoaders() { return new HashMap<>(classLoaders); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/MethodInfoClassVisitor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.ModuleVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.StringUtil; class MethodInfoClassVisitor extends ClassVisitor { private final List infos; public MethodInfoClassVisitor(List methods) { super(AsmUtils.asmVersion()); this.infos = new ArrayList<>(Objects.requireNonNull(methods).size()); for (Method method : methods) { infos.add(createMethodInfo(method)); } } private MethodParamInfo createMethodInfo(Method m) { Type[] tys = Type.getArgumentTypes(m); String[] descriptor = new String[tys.length]; for (int i = 0; i < tys.length; i++) { descriptor[i] = tys[i].getDescriptor(); } return new MethodParamInfo(m, new String[tys.length], descriptor, new String[tys.length]); } private MethodParamInfo getMethodParamInfo(String methodName, String desc) { for (MethodParamInfo mp : this.infos) { if (mp.isSameMethod(methodName, desc)) { return mp; } } return null; } @Override public MethodVisitor visitMethod(final int access, final String methodName, final String desc, final String signature, final String[] exceptions) { MethodParamInfo info = this.getMethodParamInfo(methodName, desc); if (info == null || info.getArgNames().length == 0) { return null; } return new ParseParamsMethodVisitor(AsmUtils.asmVersion(), info); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { } @Override public void visitSource(String source, String debug) { } @Override public ModuleVisitor visitModule(String name, int access, String version) { return null; } @Override public void visitNestHost(String nestHost) { } @Override public void visitOuterClass(String owner, String name, String descriptor) { } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { return null; } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { return null; } @Override public void visitAttribute(Attribute attribute) { } @Override public void visitNestMember(String nestMember) { } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { return null; } @Override public void visitEnd() { } public List getMethodInfos() { for (MethodParamInfo info : this.infos) { for (String name : info.getArgNames()) { if (StringUtil.isEmpty(name)) { Logs.asm().error("{}.{}() has empty name。params:{}", info.getMethod().getClass().getSimpleName(), info.getMethod().getName(), Arrays.toString(info.getArgNames())); throw new SumkException(362655465, "params not full parsed"); } } } return this.infos; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/MethodParamInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import java.lang.reflect.Method; import org.objectweb.asm.Type; public final class MethodParamInfo { private final Method method; private String[] argNames; private String[] descs; private String[] signatures; private final String methodDesc; public String[] getArgNames() { return argNames; } public String[] getDescs() { return descs; } public String[] getSignatures() { return signatures; } public MethodParamInfo(Method method, String[] argNames, String[] descs, String[] signatures) { this.method = method; this.argNames = argNames; this.descs = descs; this.signatures = signatures; this.methodDesc = Type.getMethodDescriptor(method); } public Method getMethod() { return method; } public String getMethodDesc() { return methodDesc; } public boolean isSameMethod(String methodName, String desc) { return methodName.equals(method.getName()) && this.methodDesc.equals(desc) && AsmUtils.sameType(Type.getArgumentTypes(desc), method.getParameterTypes()); } public Type[] getArgumentTypes() { return Type.getArgumentTypes(this.methodDesc); } public Class getDeclaringClass() { return this.method.getDeclaringClass(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/MethodPojo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Objects; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.CollectionUtil; import org.yx.util.Loader; public class MethodPojo { private final Type[] paramTypes; private final String[] paramNames; private final Class paramClz; private final ParamPojo sample; public MethodPojo(Class clz, String[] names, Type[] fieldsType) { this.paramTypes = Objects.requireNonNull(fieldsType); this.paramNames = Objects.requireNonNull(names); this.paramClz = Objects.requireNonNull(clz); try { this.sample = Loader.newInstance(this.paramClz); } catch (Exception e) { Logs.asm().error(clz.getName() + "初始化失败", e); throw new SumkException(345354334, clz.getSimpleName() + "初始化失败"); } } public List paramTypes() { return CollectionUtil.unmodifyList(paramTypes); } public List paramNames() { return CollectionUtil.unmodifyList(paramNames); } public Class paramClz() { return paramClz; } public int getIndex(String name) { for (int i = 0; i < paramNames.length; i++) { if (paramNames[i].equals(name)) { return i; } } return -1; } public Type getParamType(int index) { return this.paramTypes[index]; } public String getParamName(int index) { return this.paramNames[index]; } public ParamPojo createParamPojo(Map map) throws Exception { Object[] objs = new Object[paramNames.length]; for (int i = 0; i < objs.length; i++) { objs[i] = map.get(paramNames[i]); } ParamPojo pojo = createEmptyParamObj(); pojo.setParams(objs); return pojo; } @SuppressWarnings("unchecked") public T createEmptyParamObj() { return (T) sample.createEmpty(); } public int paramLength() { return this.paramNames.length; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ParamPojo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; public interface ParamPojo { Object[] params(); Object invoke(Object owner) throws Throwable; void setParams(Object[] objs); ParamPojo createEmpty(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ParamPojoClassFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import static org.objectweb.asm.Opcodes.AALOAD; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_SUPER; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ARETURN; import static org.objectweb.asm.Opcodes.CHECKCAST; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.GETFIELD; import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.RETURN; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.yx.conf.AppInfo; import org.yx.log.Logs; final class ParamPojoClassFactory { private static final AtomicInteger SEED = new AtomicInteger(0); final MethodParamInfo p; final Method method; final String fullName; private ClassWriter cw; private Arg[] args; public ParamPojoClassFactory(MethodParamInfo p) { this.p = p; method = p.getMethod(); fullName = "org/ytl/pojo/" + String.join("_", p.getDeclaringClass().getSimpleName(), method.getName(), "" + SEED.incrementAndGet()); } @SuppressWarnings("unchecked") public Class create() throws Exception { if (Logs.asm().isTraceEnabled()) { Logs.asm().trace("begin generate paramters pojo :{}", fullName); } cw = new ClassWriter(AppInfo.getInt("sumk.asm.writer.parampojo.compute", ClassWriter.COMPUTE_MAXS)); cw.visit(AsmUtils.jvmVersion(), ACC_PUBLIC | ACC_SUPER, fullName, null, "java/lang/Object", new String[] { "org/yx/bean/aop/asm/ParamPojo" }); this.args = new Arg[p.getArgNames().length]; for (int i = 0; i < args.length; i++) { String arg = p.getArgNames()[i]; String desc = p.getDescs()[i]; args[i] = new Arg(arg, desc); cw.visitField(ACC_PUBLIC, arg, desc, p.getSignatures()[i], null).visitEnd(); } this.buildInit(); this.buildParams(); this.buildInvoke(); this.buildCreateEmpty(); this.buildSetParams(); cw.visitEnd(); return (Class) AsmUtils.defineClass(fullName.replace('/', '.'), cw.toByteArray(), p.getDeclaringClass().getClassLoader()); } private void buildInit() { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private void buildParams() { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "params", "()[Ljava/lang/Object;", null, null); mv.visitCode(); buildArgArray(fullName, mv, args, method.getParameterTypes()); mv.visitInsn(ARETURN); mv.visitMaxs(1, 0); mv.visitEnd(); } private static void buildArgArray(String fullName, MethodVisitor mv, Arg[] args, Class[] params) { int frameIndex = 1; for (int i = 0; i < params.length; i++) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, fullName, args[i].name, args[i].desc); frameIndex += WriterHelper.storeToLocalVariable(mv, params[i], frameIndex); } WriterHelper.buildParamArray(mv, params); } private void buildCreateEmpty() { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "createEmpty", "()Lorg/yx/bean/aop/asm/ParamPojo;", null, null); mv.visitCode(); if (this.p.getArgNames().length == 0) { mv.visitVarInsn(ALOAD, 0); } else { mv.visitTypeInsn(NEW, this.fullName); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, this.fullName, "", "()V", false); } mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private void buildInvoke() { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "invoke", "(Ljava/lang/Object;)Ljava/lang/Object;", null, new String[] { "java/lang/Throwable" }); mv.visitCode(); Class ownerClz = method.getDeclaringClass(); final String ownerName = ownerClz.getName().replace('.', '/'); mv.visitLdcInsn(org.objectweb.asm.Type.getType(ownerClz)); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "cast", "(Ljava/lang/Object;)Ljava/lang/Object;", false); mv.visitTypeInsn(Opcodes.CHECKCAST, ownerName); loadObjectFields(fullName, mv, args, method.getParameterTypes()); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ownerName, method.getName(), p.getMethodDesc(), false); Class returnType = method.getReturnType(); if (void.class == method.getReturnType()) { mv.visitInsn(Opcodes.ACONST_NULL); Logs.asm().debug("{} has no return", fullName); } else if (returnType.isPrimitive()) { WriterHelper.boxPrimitive(mv, returnType); } mv.visitInsn(ARETURN); mv.visitMaxs(1, 0); mv.visitEnd(); } private static void loadObjectFields(String fullName, MethodVisitor mv, Arg[] args, Class[] params) { for (int i = 0; i < params.length; i++) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, fullName, args[i].name, args[i].desc); } } private void buildSetParams() { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "setParams", "([Ljava/lang/Object;)V", null, null); mv.visitCode(); for (int i = 0; i < args.length; i++) { Arg arg = args[i]; Class argType = method.getParameterTypes()[i]; Label label1 = new Label(); if (argType.isPrimitive()) { mv.visitVarInsn(ALOAD, 1); WriterHelper.visitInt(mv, i); mv.visitInsn(AALOAD); mv.visitJumpInsn(IFNULL, label1); } mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); WriterHelper.visitInt(mv, i); mv.visitInsn(AALOAD); checkCast(mv, argType, arg.desc); mv.visitFieldInsn(PUTFIELD, this.fullName, arg.name, arg.desc); if (argType.isPrimitive()) { mv.visitLabel(label1); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } } mv.visitInsn(RETURN); mv.visitMaxs(1, 2); mv.visitEnd(); } private void checkCast(MethodVisitor mv, Class argType, String desc) { if (!argType.isPrimitive()) { mv.visitTypeInsn(CHECKCAST, argType.getName().replace('.', '/')); return; } if (argType == char.class) { mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); return; } if (argType == boolean.class) { mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); return; } mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", argType.getName() + "Value", "()" + desc, false); } static class Arg { final String name; final String desc; private Arg(String name, String desc) { this.name = name; this.desc = desc; } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ParamPojos.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import java.lang.reflect.Type; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ParamPojos { private static Map, MethodPojo> map = new ConcurrentHashMap<>(); public static MethodPojo get(Class clz) { return map.get(clz); } public synchronized static MethodPojo create(MethodParamInfo p) throws Exception { Class clz = new ParamPojoClassFactory(p).create(); MethodPojo info = map.get(clz); if (info != null) { return info; } Type[] fs = new Type[p.getArgNames().length]; for (int i = 0; i < fs.length; i++) { fs[i] = clz.getDeclaredField(p.getArgNames()[i]).getGenericType(); } info = new MethodPojo(clz, p.getArgNames(), fs); map.put(info.paramClz(), info); return info; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ParseParamsMethodVisitor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.slf4j.Logger; import org.yx.exception.SumkException; import org.yx.log.Logs; public class ParseParamsMethodVisitor extends MethodVisitor { private final MethodParamInfo info; private final List argPojos; private final int maxIndex; public ParseParamsMethodVisitor(int api, MethodParamInfo info) { super(api); this.info = info; this.maxIndex = info.getArgNames().length * 2; this.argPojos = new ArrayList<>(maxIndex); } @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { if (index == 0 && "this".equals(name)) { return; } if (index >= maxIndex) { return; } argPojos.add(new LocalArg(name, desc, index, signature)); } @Override public void visitEnd() { Logger log = Logs.asm(); String methodName = info.getMethod().getName(); Type[] args = info.getArgumentTypes(); Collections.sort(argPojos); if (log.isTraceEnabled()) { log.trace("{}.{}():{}", info.getMethod().getDeclaringClass().getName(), methodName, argPojos); } String[] argNames = info.getArgNames(); String[] signatures = info.getSignatures(); int size = argNames.length; if (argPojos.size() < size) { log.error("{}.{},real param size:{},but is {}", info.getMethod().getDeclaringClass().getName(), methodName, size, argPojos.size()); throw new SumkException(9123253, "failed to parse parameter because parameter size not satisfied"); } for (int i = 0; i < size; i++) { LocalArg pojo = argPojos.get(i); if (!args[i].getDescriptor().equals(pojo.desc)) { log.error("{}.{},i:{},index:{},except:{},indeed:{}", info.getMethod().getDeclaringClass().getName(), methodName, i, pojo.index, args[i].getDescriptor(), pojo.desc); throw new SumkException(9123253, "failed to parse parameter"); } argNames[i] = pojo.name; signatures[i] = pojo.signature; } } private static class LocalArg implements Comparable { final String name; final String desc; final int index; final String signature; public LocalArg(String name, String desc, int index, String signature) { this.name = name; this.desc = desc; this.index = index; this.signature = signature; } @Override public int compareTo(LocalArg o) { return index - o.index; } @Override public String toString() { return "{name=" + name + ", desc=" + desc + ", index=" + index + ", signature=" + signature + "}"; } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ProxyClassLoader.java ================================================ package org.yx.bean.aop.asm; public class ProxyClassLoader extends ClassLoader { public ProxyClassLoader(ClassLoader parent) { super(parent); } public Class defineClass(String name, byte[] clzByts) { return super.defineClass(name, clzByts, 0, clzByts.length); } @Override public String toString() { return "ProxyClassLoader [parent :" + getParent() + "]@" + Integer.toHexString(hashCode()); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ProxyClassVistor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ARETURN; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.RETURN; import java.lang.reflect.Method; import java.util.Map; import java.util.Map.Entry; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; import org.yx.bean.aop.asm.ProxyMethodWritor.AsmMethod; public class ProxyClassVistor extends ClassVisitor { private final Class orginClz; private final Map aopMethods; private final String clzName; public ProxyClassVistor(final ClassVisitor cv, String newClzName, Class clz, Map aopMethods) { super(AsmUtils.asmVersion(), cv); this.orginClz = clz; this.aopMethods = aopMethods; this.clzName = newClzName; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, clzName.replace('.', '/'), signature, name, new String[] { "org/yx/bean/Boxed" }); MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, name, "", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private Entry queryMethod(String name, String desc) { for (Entry entry : aopMethods.entrySet()) { Method m = entry.getKey(); if (m.getName().equals(name) && desc.equals(Type.getMethodDescriptor(m))) { return entry; } } return null; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ("".equals(name)) { return null; } int badModifiers = ACC_STATIC | ACC_FINAL | ACC_ABSTRACT | ACC_PRIVATE; if ((access & badModifiers) != 0) { return null; } Entry methodEntry = queryMethod(name, desc); if (methodEntry != null) { MethodVisitor mv = cv.visitMethod(access, name, desc, signature, null); AsmMethod asmMethod = new AsmMethod(access, name, desc, signature, exceptions, methodEntry.getKey(), methodEntry.getValue().intValue(), clzName, this.orginClz); ProxyMethodWritor.write(mv, asmMethod); return null; } return null; } @Override public void visitOuterClass(String owner, String name, String desc) { } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { return null; } @Override public void visitAttribute(Attribute attr) { } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } @Override public void visitEnd() { MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "targetRawClass", "()Ljava/lang/Class;", "()Ljava/lang/Class<*>;", null); mv.visitCode(); mv.visitLdcInsn(Type.getType(this.orginClz)); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); super.visitEnd(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/ProxyMethodWritor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import static org.objectweb.asm.Opcodes.ACONST_NULL; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ARETURN; import static org.objectweb.asm.Opcodes.ASTORE; import static org.objectweb.asm.Opcodes.ATHROW; import static org.objectweb.asm.Opcodes.DRETURN; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.FRETURN; import static org.objectweb.asm.Opcodes.ICONST_0; import static org.objectweb.asm.Opcodes.ICONST_1; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.IRETURN; import static org.objectweb.asm.Opcodes.LRETURN; import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.RETURN; import static org.yx.bean.aop.asm.WriterHelper.SINGLE; import static org.yx.bean.aop.asm.WriterHelper.WIDTH; import static org.yx.bean.aop.asm.WriterHelper.buildParamArray; import static org.yx.bean.aop.asm.WriterHelper.loadFromLocalVariable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; public final class ProxyMethodWritor { private static void jReturn(MethodVisitor mv, Class c) { if (c == Integer.TYPE || c == Boolean.TYPE || c == Byte.TYPE || c == Character.TYPE || c == Short.TYPE) { mv.visitInsn(IRETURN); } else if (c == Float.TYPE) { mv.visitInsn(FRETURN); } else if (c == Double.TYPE) { mv.visitInsn(DRETURN); } else if (c == Long.TYPE) { mv.visitInsn(LRETURN); } else { mv.visitInsn(ARETURN); } } private static int argLength(Class[] params) { int size = 0; for (Class param : params) { size += (param == Double.TYPE || param == Long.TYPE) ? WIDTH : SINGLE; } return size; } private static class ProxyBuilder { private static final String AOP_EXCUTOR_CHAIN = "org/yx/bean/aop/AopExecutorChain"; private final MethodVisitor mv; private final AsmMethod asmMethod; private final Class[] params; private final Class returnType; private final String superowener; private final int aopExcutorChainIndex; private void jReturn() { ProxyMethodWritor.jReturn(mv, returnType); } private void jReturnNULLOrException() { if (!returnType.isPrimitive()) { mv.visitInsn(ACONST_NULL); mv.visitInsn(ARETURN); } else { mv.visitTypeInsn(NEW, "org/yx/exception/SumkException"); mv.visitInsn(DUP); mv.visitLdcInsn(new Integer(-912753901)); mv.visitLdcInsn("返回值为原始类型的方法,不支持aop屏蔽异常"); mv.visitMethodInsn(INVOKESPECIAL, "org/yx/exception/SumkException", "", "(ILjava/lang/String;)V", false); mv.visitInsn(ATHROW); } } private int storeReuturnToLocalVariable(int frameIndex) { return WriterHelper.storeToLocalVariable(mv, returnType, frameIndex); } private void loadArgs() { int frameIndex = 1; for (Class argType : params) { frameIndex += loadFromLocalVariable(mv, argType, frameIndex, false); } } public ProxyBuilder(MethodVisitor mv, AsmMethod asmMethod) { this.asmMethod = asmMethod; this.mv = mv; params = asmMethod.method.getParameterTypes(); returnType = asmMethod.method.getReturnType(); superowener = Type.getInternalName(asmMethod.superClz); int paramsVariableIndex = argLength(params); aopExcutorChainIndex = paramsVariableIndex + 1; } private void callSuperMethod() { mv.visitVarInsn(ALOAD, 0); this.loadArgs(); mv.visitMethodInsn(INVOKESPECIAL, superowener, asmMethod.name, asmMethod.desc, false); } private void writeVoidMethod(int key) { Label l3 = new Label(); Label label_before_1 = new Label(); Label label_biz_executed = new Label(); mv.visitTryCatchBlock(label_before_1, label_biz_executed, l3, "java/lang/Throwable"); WriterHelper.visitInt(mv, key); mv.visitMethodInsn(INVOKESTATIC, "org/yx/bean/InnerIOC", "getAopExecutorChain", "(I)Lorg/yx/bean/aop/AopExecutorChain;", false); mv.visitVarInsn(ASTORE, this.aopExcutorChainIndex); mv.visitLabel(label_before_1); mv.visitVarInsn(ALOAD, this.aopExcutorChainIndex); buildParamArray(mv, params); this.visitBefore(); this.callSuperMethod(); mv.visitLabel(label_biz_executed); mv.visitVarInsn(ALOAD, this.aopExcutorChainIndex); mv.visitInsn(ACONST_NULL); mv.visitInsn(ACONST_NULL); mv.visitInsn(ICONST_1); this.visitAfter(); mv.visitInsn(RETURN); mv.visitLabel(l3); this.visitFullFrame(); int exceptionIndex = this.aopExcutorChainIndex + 1; mv.visitVarInsn(ASTORE, exceptionIndex); mv.visitVarInsn(ALOAD, this.aopExcutorChainIndex); mv.visitInsn(ACONST_NULL); mv.visitVarInsn(ALOAD, exceptionIndex); mv.visitInsn(ICONST_0); visitAfter(); mv.visitInsn(RETURN); } public void write() { int index = this.asmMethod.excutorSupplierIndex; mv.visitCode(); boolean hasReturn = returnType != null && returnType != Void.TYPE; if (hasReturn) { this.writeWithReturn(index); } else { this.writeVoidMethod(index); } mv.visitMaxs(1, 1); mv.visitEnd(); } private void visitAfter() { mv.visitMethodInsn(INVOKEVIRTUAL, AOP_EXCUTOR_CHAIN, "after", "(Ljava/lang/Object;Ljava/lang/Throwable;Z)V", false); } private void visitBefore() { mv.visitMethodInsn(INVOKEVIRTUAL, AOP_EXCUTOR_CHAIN, "before", "([Ljava/lang/Object;)V", false); } private void writeWithReturn(int key) { Label l3 = new Label(); Label label_before_1 = new Label(); Label label_biz_executed = new Label(); mv.visitTryCatchBlock(label_before_1, label_biz_executed, l3, "java/lang/Throwable"); WriterHelper.visitInt(mv, key); mv.visitMethodInsn(INVOKESTATIC, "org/yx/bean/InnerIOC", "getAopExecutorChain", "(I)Lorg/yx/bean/aop/AopExecutorChain;", false); mv.visitVarInsn(ASTORE, this.aopExcutorChainIndex); mv.visitLabel(label_before_1); mv.visitVarInsn(ALOAD, this.aopExcutorChainIndex); buildParamArray(mv, params); this.visitBefore(); this.callSuperMethod(); mv.visitLabel(label_biz_executed); int returnValueIndex = this.aopExcutorChainIndex + 1; storeReuturnToLocalVariable(returnValueIndex); mv.visitVarInsn(ALOAD, this.aopExcutorChainIndex); loadFromLocalVariable(mv, this.returnType, returnValueIndex, true); mv.visitInsn(ACONST_NULL); mv.visitInsn(ICONST_1); visitAfter(); loadFromLocalVariable(mv, this.returnType, this.aopExcutorChainIndex + 1, false); this.jReturn(); int exceptionIndex = this.aopExcutorChainIndex + 1; mv.visitLabel(l3); this.visitFullFrame(); mv.visitVarInsn(ASTORE, exceptionIndex); mv.visitVarInsn(ALOAD, this.aopExcutorChainIndex); mv.visitInsn(ACONST_NULL); mv.visitVarInsn(ALOAD, exceptionIndex); mv.visitInsn(ICONST_0); visitAfter(); this.jReturnNULLOrException(); } private void visitFullFrame() { String currentClz = asmMethod.currentClz.replace('.', '/'); List argTypes = AsmUtils.getImplicitFrame( asmMethod.desc.substring(asmMethod.desc.indexOf("(") + 1, asmMethod.desc.indexOf(")"))); List list = new ArrayList<>(); list.add(currentClz); list.addAll(argTypes); list.add(AOP_EXCUTOR_CHAIN); Object[] frames = list.toArray(new Object[list.size()]); mv.visitFrame(Opcodes.F_FULL, frames.length, frames, 1, new Object[] { "java/lang/Throwable" }); } } public static void write(MethodVisitor mv, AsmMethod asmMethod) { new ProxyBuilder(mv, asmMethod).write(); } static class AsmMethod { final int access; final String name; final String desc; final String signature; final String[] exceptions; final Method method; final String currentClz; final Class superClz; final int excutorSupplierIndex; public AsmMethod(int access, String name, String desc, String signature, String[] exceptions, Method method, int advisors, String currentClz, Class supperClz) { this.access = access; this.name = name; this.desc = desc; this.signature = signature; this.exceptions = exceptions; this.method = method; this.currentClz = currentClz; this.superClz = supperClz; this.excutorSupplierIndex = advisors; } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/asm/WriterHelper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.asm; import static org.objectweb.asm.Opcodes.AASTORE; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ANEWARRAY; import static org.objectweb.asm.Opcodes.ASTORE; import static org.objectweb.asm.Opcodes.BIPUSH; import static org.objectweb.asm.Opcodes.DLOAD; import static org.objectweb.asm.Opcodes.DSTORE; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.FLOAD; import static org.objectweb.asm.Opcodes.FSTORE; import static org.objectweb.asm.Opcodes.ICONST_0; import static org.objectweb.asm.Opcodes.ILOAD; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.ISTORE; import static org.objectweb.asm.Opcodes.LLOAD; import static org.objectweb.asm.Opcodes.LSTORE; import static org.objectweb.asm.Opcodes.SIPUSH; import org.objectweb.asm.MethodVisitor; import org.yx.conf.AppInfo; public final class WriterHelper { public static final int SINGLE = AppInfo.getInt("sumk.asm.single", 1); public static final int WIDTH = AppInfo.getInt("sumk.asm.width", 2); public static void visitInt(MethodVisitor mv, int num) { if (num >= -1 && num <= 5) { mv.visitInsn(ICONST_0 + num); return; } if (num <= Byte.MAX_VALUE && num >= Byte.MIN_VALUE) { mv.visitIntInsn(BIPUSH, num); return; } if (num <= Short.MAX_VALUE && num >= Short.MIN_VALUE) { mv.visitIntInsn(SIPUSH, num); return; } mv.visitLdcInsn(num); } public static void buildParamArray(MethodVisitor mv, Class[] params) { final int len = params == null ? 0 : params.length; visitInt(mv, len); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); if (len == 0) { return; } int frameIndex = 1; for (int i = 0; i < len; i++) { mv.visitInsn(DUP); visitInt(mv, i); Class argType = params[i]; frameIndex += loadFromLocalVariable(mv, argType, frameIndex, true); mv.visitInsn(AASTORE); } } public static int loadFromLocalVariable(MethodVisitor mv, Class c, int frameIndex, boolean autoBox) { if (c == Integer.TYPE) { mv.visitVarInsn(ILOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); } return SINGLE; } if (c == Boolean.TYPE) { mv.visitVarInsn(ILOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); } return SINGLE; } if (c == Byte.TYPE) { mv.visitVarInsn(ILOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); } return SINGLE; } if (c == Character.TYPE) { mv.visitVarInsn(ILOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); } return SINGLE; } if (c == Short.TYPE) { mv.visitVarInsn(ILOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); } return SINGLE; } if (c == Float.TYPE) { mv.visitVarInsn(FLOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); } return SINGLE; } if (c == Double.TYPE) { mv.visitVarInsn(DLOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); } return WIDTH; } if (c == Long.TYPE) { mv.visitVarInsn(LLOAD, frameIndex); if (autoBox) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); } return WIDTH; } mv.visitVarInsn(ALOAD, frameIndex); return SINGLE; } public static int boxPrimitive(MethodVisitor mv, Class c) { if (c == Integer.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); return SINGLE; } if (c == Boolean.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); return SINGLE; } if (c == Byte.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); return SINGLE; } if (c == Character.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); return SINGLE; } if (c == Short.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); return SINGLE; } if (c == Float.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); return SINGLE; } if (c == Double.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); return WIDTH; } if (c == Long.TYPE) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); return WIDTH; } return 0; } public static int storeToLocalVariable(MethodVisitor mv, Class c, int frameIndex) { if (c == Integer.TYPE || c == Boolean.TYPE || c == Byte.TYPE || c == Character.TYPE || c == Short.TYPE) { mv.visitVarInsn(ISTORE, frameIndex); return SINGLE; } if (c == Float.TYPE) { mv.visitVarInsn(FSTORE, frameIndex); return SINGLE; } if (c == Double.TYPE) { mv.visitVarInsn(DSTORE, frameIndex); return WIDTH; } if (c == Long.TYPE) { mv.visitVarInsn(LSTORE, frameIndex); return WIDTH; } mv.visitVarInsn(ASTORE, frameIndex); return SINGLE; } public static String boxDesc(String desc) { switch (desc.charAt(0)) { case 'Z': return "Ljava/lang/Boolean;"; case 'C': return "Ljava/lang/Character;"; case 'B': return "Ljava/lang/Byte;"; case 'S': return "Ljava/lang/Short;"; case 'I': return "Ljava/lang/Integer;"; case 'F': return "Ljava/lang/Float;"; case 'J': return "Ljava/lang/Long;"; case 'D': return "Ljava/lang/Double;"; default: return desc; } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/context/CalleeNode.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.context; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Objects; import org.yx.annotation.doc.Comment; import org.yx.annotation.spec.ParamSpec; import org.yx.annotation.spec.Specs; import org.yx.bean.aop.asm.ParamPojo; import org.yx.common.validate.FieldParameterHolder; import org.yx.common.validate.ParamInfo; import org.yx.common.validate.Validators; import org.yx.bean.aop.asm.MethodPojo; import org.yx.log.Log; import org.yx.util.CollectionUtil; import org.yx.util.SumkThreadPool; public abstract class CalleeNode { protected final ParamInfo[] paramInfos; protected final Object owner; protected final MethodPojo params; private final int toplimit; protected final Method method; public CalleeNode(Object owner, Method method, MethodPojo params, int toplimit) { this.owner = Objects.requireNonNull(owner); this.params = Objects.requireNonNull(params); this.method = Objects.requireNonNull(method); ParamSpec[] paramSpecs = Specs.extractParamParamter(method); this.paramInfos = paramSpecs == null || paramSpecs.length == 0 ? null : new ParamInfo[paramSpecs.length]; this.toplimit = toplimit; if (this.paramInfos != null) { Class[] argTypes = this.getParameterTypes(); for (int i = 0; i < this.paramInfos.length; i++) { ParamSpec p = paramSpecs[i]; if (p == null) { continue; } paramInfos[i] = new ParamInfo(p, params.getParamName(i), argTypes[i]); } } registerFieldInfos(); } protected void registerFieldInfos() { if (this.paramInfos == null) { return; } for (ParamInfo pf : this.paramInfos) { if (pf == null || !pf.isComplex()) { continue; } FieldParameterHolder.registerFieldInfo(pf.getParamType()); } } public boolean overflowThreshold() { if (this.toplimit < SumkThreadPool.executor().threshold()) { if (Log.get("sumk.thread").isDebugEnabled()) { String msg = new StringBuilder().append("[") .append(this.getClass().getSimpleName().replace("ActionNode", "")).append("] ") .append(this.method.getDeclaringClass().getSimpleName()).append(".") .append(this.method.getName()).append("() - priority=").append(toplimit) .append(" , threshold=").append(SumkThreadPool.executor().threshold()).toString(); Log.get("sumk.thread").debug(msg); } return true; } return false; } public ParamPojo createEmptyParamObj() { return this.params.createEmptyParamObj(); } public Class getDeclaringClass() { return this.method.getDeclaringClass(); } public Class getReturnType() { return this.method.getReturnType(); } public String getMethodName() { return this.method.getName(); } public Class[] getParameterTypes() { return method.getParameterTypes(); } public Method rawMethod() { return this.method; } public T getAnnotation(Class annotationClass) { return method.getAnnotation(annotationClass); } public Annotation[] getDeclaredAnnotations() { return method.getDeclaredAnnotations(); } public Annotation[][] getParameterAnnotations() { return method.getParameterAnnotations(); } public AnnotatedType getAnnotatedReturnType() { return method.getAnnotatedReturnType(); } public int toplimit() { return this.toplimit; } public int paramLength() { return this.params.paramLength(); } public Object owner() { return this.owner; } public Object execute(ParamPojo argObj) throws Throwable { if (this.paramInfos != null) { Object[] params = argObj.params(); for (int i = 0; i < paramInfos.length; i++) { ParamInfo info = paramInfos[i]; if (info != null) { Validators.check(info, params[i]); } } } try { return argObj.invoke(owner); } catch (Throwable e) { if (e instanceof InvocationTargetException) { InvocationTargetException te = (InvocationTargetException) e; if (te.getTargetException() != null) { throw te.getTargetException(); } } throw e; } } public List paramNames() { return this.params.paramNames(); } public List paramInfos() { return CollectionUtil.unmodifyList(paramInfos); } public MethodPojo params() { return this.params; } public String comment() { Comment c = getAnnotation(Comment.class); return c == null ? "" : c.value(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/aop/context/NodeContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.aop.context; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.yx.annotation.doc.NotNull; import org.yx.bean.aop.asm.ParamPojo; public abstract class NodeContext { private ParamPojo argPojo; @NotNull public abstract T node(); public ParamPojo getParamPojo() { return argPojo; } public void setParamPojo(ParamPojo argPojo) { this.argPojo = Objects.requireNonNull(argPojo); } /** * @return 参数组成的map对象 */ public Map getParams() { Object[] args = argPojo.params(); int len = args.length; List names = node().paramNames(); Map map = new LinkedHashMap<>(); for (int i = 0; i < len; i++) { String name = names.get(i); map.put(name, args[i]); } return map; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/watcher/BeanCreateWatcher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.watcher; import java.util.List; import org.yx.base.Ordered; public interface BeanCreateWatcher extends Ordered { void afterCreate(List beans); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/watcher/BeanInjectWatcher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.bean.watcher; import java.util.List; import org.yx.base.Ordered; /** * 只被调用一次,beans参数是额外参数,如果用不到可以忽略它。 beans里的对象不一定是原始对象,有可能是代理后的对象, * 可通过是否实现Boxed接口来判断。 */ public interface BeanInjectWatcher extends Ordered { void afterInject(List beans); } ================================================ FILE: sumk-framework/src/main/java/org/yx/bean/watcher/BootWatcher.java ================================================ package org.yx.bean.watcher; import java.util.List; import java.util.function.Predicate; import org.yx.base.Ordered; /** * 实现这个接口的类,必须要有一个无参的构造函数,这个接口与Bean无关,不需要@Bean注解。
* ioc启动主要由本对象的publish方法完成,本接口的实例不属于IOC的bean。
* 如果想要排除掉某个BootWatcher,可以通过sumk.ioc.booter.exclude配置来实现, * 也可以通过sumk.ioc.exclude.XX来配置 */ public interface BootWatcher extends Ordered { /** * ioc启动工作,这个阶段还不能使用数据库、redis等功能 * * @param sortedClasses 扫描出来的class列表,这个列表已经排好优先级顺序了。只读 * @param optional 如果加载失败就忽略的类名的判定器 * @return 修改后的class列表,或者null。一般返回null就行了 * @throws Exception 如果抛出异常,就表示启动失败 */ List> publish(List> sortedClasses, Predicate optional) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/Host.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common; import java.net.InetSocketAddress; import java.util.Objects; public final class Host implements Comparable { private final String ip; private final int port; private Host(String ip, int port) { this.ip = Objects.requireNonNull(ip); this.port = port; } public static Host create(String addr) { int index = addr.lastIndexOf(':'); if (index < 1) { return null; } return new Host(addr.substring(0, index), Integer.valueOf(addr.substring(index + 1))); } public static Host create(String ip, int port) { return new Host(ip, port); } public String ip() { return ip; } public int port() { return port; } public InetSocketAddress toInetSocketAddress() { return new InetSocketAddress(ip, port); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((ip == null) ? 0 : ip.hashCode()); result = prime * result + port; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Host other = (Host) obj; if (ip == null) { if (other.ip != null) return false; } else if (!ip.equals(other.ip)) return false; if (port != other.port) return false; return true; } public String toAddressString() { return new StringBuilder().append(ip).append(':').append(port).toString(); } /** * 返回 ip + ":" + port格式, 这个方法也比较重要,所以它的格式不会发生变更 */ @Override public String toString() { return toAddressString(); } @Override public int compareTo(Host h) { int t = ip.compareTo(h.ip); if (t != 0) { return t; } return port - h.port; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/Predicator.java ================================================ package org.yx.common; import java.util.List; import java.util.function.Predicate; import org.yx.util.StringUtil; public class Predicator { public static boolean test(String vs, Predicate p) { if (vs.contains("||")) { return orTest(vs, p); } return andTest(vs, p); } public static boolean andTest(String vs, Predicate p) { List list = StringUtil.splitAndTrim(vs, ",", ",", "&&"); for (String s : list) { if (!p.test(s)) { return false; } } return true; } public static boolean orTest(String vs, Predicate p) { List list = StringUtil.splitAndTrim(vs, "\\|\\|"); for (String s : list) { if (p.test(s)) { return true; } } return false; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/StringEntity.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common; import java.util.Objects; /** * 这个对象是不可修改的 */ public final class StringEntity { private final String key; private final T value; private StringEntity(String key, T value) { this.key = Objects.requireNonNull(key); this.value = value; } public static StringEntity create(String key, T value) { return new StringEntity<>(key, value); } public String key() { return key; } public T value() { return value; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StringEntity other = (StringEntity) obj; if (key == null) { if (other.key != null) return false; } else if (!key.equals(other.key)) return false; if (value == null) { if (other.value != null) return false; } else if (!value.equals(other.value)) return false; return true; } @Override public String toString() { return "[key=" + key + ", value=" + value + "]"; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/action/ActInfoUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.action; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.yx.annotation.ExcludeFromParams; import org.yx.annotation.ExcludeFromResponse; import org.yx.annotation.spec.ParamSpec; import org.yx.annotation.spec.Specs; import org.yx.bean.aop.context.CalleeNode; import org.yx.common.util.S; import org.yx.common.validate.FieldParameterHolder; import org.yx.common.validate.FieldParameterInfo; import org.yx.common.validate.ManuParameterInfo; import org.yx.common.validate.ParameterInfo; import org.yx.common.validate.Validators; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.util.SumkDate; public final class ActInfoUtil { public static Object describe(Class clazz, Class exclude) { if (clazz.isArray()) { return new Object[] { describe(clazz.getComponentType(), exclude) }; } if (clazz.isPrimitive() || clazz.getName().startsWith("java.") || clazz == SumkDate.class) { return clazz.getSimpleName(); } if (Map.class.isAssignableFrom(clazz)) { return Collections.emptyMap(); } if (Collection.class.isAssignableFrom(clazz)) { return Collections.emptyList(); } if (clazz.isAnnotationPresent(exclude)) { Logs.http().warn("{}被{}注解了,可能引起一些奇怪的业务反应", clazz.getName(), exclude.getSimpleName()); return null; } Map map = new LinkedHashMap<>(); Field[] fs = S.bean().getFields(clazz); if (AppInfo.getBoolean("sumk.http.act.output.class", false)) { map.put("$class", clazz.getName()); } for (Field f : fs) { if (f.isAnnotationPresent(exclude) || f.getType().isAnnotationPresent(exclude)) { continue; } map.putIfAbsent(f.getName(), describe(f.getType(), exclude)); } return map; } private static Map createMap(String name, CalleeNode node) { Map map = new LinkedHashMap<>(); map.put("name", name); map.put("class", node.getDeclaringClass().getName()); map.put("method", node.getMethodName()); map.put("resultType", node.rawMethod().getGenericReturnType().getTypeName()); return map; } public static Map simpleInfoMap(String name, CalleeNode node) { Map map = createMap(name, node); Map param = new LinkedHashMap<>(); int paramSize = node.paramLength(); Class[] paramTypes = node.getParameterTypes(); for (int i = 0; i < paramSize; i++) { Class paramType = paramTypes[i]; param.put(node.params().getParamName(i), describe(paramType, ExcludeFromParams.class)); } map.put("params", param); map.put("result", describe(node.getReturnType(), ExcludeFromResponse.class)); return map; } public static Map fullInfoMap(String name, CalleeNode node) { Map map = createMap(name, node); List list = new ArrayList<>(); if (node.paramInfos() != null) { int paramSize = node.paramLength(); Class[] paramTypes = node.getParameterTypes(); for (int i = 0; i < paramSize; i++) { Class paramType = paramTypes[i]; ParameterInfo pi = node.paramInfos().get(i); if (pi == null) { ManuParameterInfo mp = new ManuParameterInfo(); mp.setParamType(paramType); mp.setComplex(false); mp.setParamName(node.params().getParamName(i)); pi = mp; } boolean supportComplex = pi == null ? false : pi.isComplex(); ParamDescript pd = fullDescribe(paramType, pi, supportComplex, ExcludeFromParams.class); list.add(pd); } } map.put("params", list); Class returnClz = node.getReturnType(); ManuParameterInfo mp = new ManuParameterInfo(); mp.setParamType(returnClz); if (!returnClz.isPrimitive()) { mp.setComplex(true); } map.put("result", fullDescribe(returnClz, mp, false, ExcludeFromResponse.class)); return map; } public static ParamDescript fullDescribe(Class clazz, ParameterInfo info, boolean supportComplex, Class exclude) { if (clazz.isArray()) { ParamDescript pd = fullDescribe(clazz.getComponentType(), info, supportComplex, exclude); pd.setType(pd.getType() + "[]"); pd.setArray(true); return pd; } ParamDescript pd = new ParamDescript(); if (!Validators.supportComplex(clazz)) { return pd.copyFrom(info, true).setType(clazz); } pd.copyFrom(info, supportComplex).setType(clazz); if (clazz.isAnnotationPresent(exclude)) { Logs.http().warn("{}被{}注解了,可能引起一些奇怪的业务反应", clazz.getName(), exclude.getSimpleName()); return null; } List list = new ArrayList<>(); Field[] fs = S.bean().getFields(clazz); Map infoMap = FieldParameterHolder.getFieldParameterMap(clazz); for (Field f : fs) { if (f.isAnnotationPresent(exclude) || f.getType().isAnnotationPresent(exclude)) { continue; } ParameterInfo fp = infoMap.get(f); if (fp == null) { ParamSpec param = Specs.extractParamField(f); if (param != null) { fp = new FieldParameterInfo(param, f); } else { ManuParameterInfo mp = new ManuParameterInfo(); mp.setParamName(f.getName()); mp.setParamType(f.getType()); fp = mp; } } if (!Validators.supportComplex(f.getType())) { ParamDescript leaf = new ParamDescript().copyFrom(fp, supportComplex).setType(f.getType()); list.add(leaf); continue; } list.add(fullDescribe(f.getType(), fp, supportComplex && fp.isComplex(), exclude)); } pd.setComplexFields(list); return pd; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/action/ActionStatis.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.action; import java.util.Map; public interface ActionStatis { void visit(String name, long time, boolean success); Map getAndReset(); Map getAll(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/action/ActionStatisImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.action; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; public class ActionStatisImpl implements ActionStatis { private Map actStatis; public ActionStatisImpl(Map actStatis) { this.actStatis = actStatis; } public ActionStatisImpl() { this.actStatis = new ConcurrentHashMap<>(); } public void setActStatis(Map actStatis) { this.actStatis = Objects.requireNonNull(actStatis); } public void visit(String name, long time, boolean success) { StatisItem statis = actStatis.get(name); if (statis == null) { statis = new StatisItem(name); StatisItem tmp = actStatis.putIfAbsent(name, statis); if (tmp != null) { statis = tmp; } } if (success) { statis.successVisit(time); } else { statis.failedVisit(time); } } public Map getAndReset() { Map tmp = this.actStatis; actStatis = new ConcurrentHashMap<>(); return tmp; } public Map getAll() { return this.actStatis; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/action/ParamDescript.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.action; import java.util.List; import org.yx.common.validate.ParameterInfo; public class ParamDescript { private String name; private String cnName; private Boolean required; private Integer max; private Integer min; private String type; private String example; private String comment; private Object custom; private Boolean complex; private List fields; private Boolean array; public List getComplexFields() { return fields; } public void setComplexFields(List list) { this.fields = list != null && list.size() > 0 ? list : null; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCnName() { return cnName; } public void setCnName(String cnName) { this.cnName = cnName; } public Boolean getRequired() { return required; } public void setRequired(Boolean required) { this.required = required; } public Integer getMax() { return max; } public void setMax(Integer max) { this.max = max; } public Integer getMin() { return min; } public void setMin(Integer min) { this.min = min; } public String getType() { return type; } public ParamDescript setType(String type) { this.type = type; return this; } public ParamDescript setType(Class type) { String subfix = ""; while (type.isArray()) { subfix += "[]"; type = type.getComponentType(); } this.type = type.getName() + subfix; return this; } public String getExample() { return example; } public void setExample(String example) { this.example = example; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public Object getCustom() { return custom; } public void setCustom(String custom) { this.custom = custom; } public Boolean getComplex() { return complex; } public void setComplex(Boolean complex) { this.complex = complex; } public Boolean getArray() { return array; } public void setArray(Boolean array) { this.array = array; } public ParamDescript copyFrom(ParameterInfo info, boolean supportComplex) { if (info == null) { return this; } this.cnName = getValue(info.getCnName()); this.name = getValue(info.getParamName()); this.comment = getValue(info.comment()); this.custom = info.custom(); this.example = getValue(info.example()); this.required = supportComplex && info.isRequired() ? true : null; this.complex = supportComplex && info.isComplex() ? true : null; this.max = supportComplex && info.getMax() >= 0 ? info.getMax() : null; this.min = supportComplex && info.getMin() >= 0 ? info.getMin() : null; return this; } private String getValue(String v) { return v == null || v.isEmpty() ? null : v; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/action/StatisItem.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.action; import java.util.Objects; public class StatisItem { private final String name; private int successCount; private long successTime; private int failedCount; private long failedTime; public StatisItem(String name) { this.name = Objects.requireNonNull(name); } public void successVisit(long t) { successCount++; successTime += t; } public void failedVisit(long t) { failedCount++; failedTime += t; } @Override public String toString() { return name + ": count=" + getSuccessCount() + ", time=" + getSuccessTime() + ", failedCount=" + getFailedCount() + ", failedTime=" + getFailedTime(); } public String toSimpleString() { long c = getSuccessCount(); long t = getSuccessTime(); double avg = c == 0 ? 0 : t * 1d / c; return String.join(" ", name, String.valueOf(c), String.valueOf(t), String.valueOf(Math.round(avg)), String.valueOf(getFailedCount()), String.valueOf(getFailedTime())); } public static String header() { return "name success time avg failed failedTime"; } public String getName() { return name; } public long getSuccessCount() { return Integer.toUnsignedLong(this.successCount); } public long getSuccessTime() { return this.successTime; } public long getFailedCount() { return Integer.toUnsignedLong(this.failedCount); } public long getFailedTime() { return this.failedTime; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/AndExpression.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.function.Predicate; import org.yx.exception.SumkException; public class AndExpression implements Predicate> { private final Predicate>[] exps; @SuppressWarnings("unchecked") public AndExpression(Collection>> exps) { if (exps == null || exps.isEmpty()) { throw new SumkException(3455635, "ParamExpression列表不能为空"); } this.exps = exps.toArray(new Predicate[exps.size()]); } @Override public boolean test(Map map) { for (Predicate> exp : exps) { if (!exp.test(map)) { return false; } } return true; } @Override public String toString() { return "AND" + Arrays.toString(exps); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/Expressions.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Collection; import java.util.Map; import java.util.function.Predicate; import org.yx.exception.SumkException; public class Expressions { public static SimpleExpression createSimpleExpression(String key, String matchType) { if (key == null || (key = key.trim()).isEmpty()) { throw new SumkException(345565, "参数有误:" + key); } switch (matchType) { case MatchType.FALSE_BY_NULL: return new NotNullExpression(key); case MatchType.FALSE_BY_NOKEY: return new HasKeyExpression(key); case MatchType.FALSE_BY_EMPTY: return new NotEmptyExpression(key); default: throw new SumkException(3455651, matchType + "类型不正确"); } } public static Predicate> booleanExpression(Collection>> exps, boolean and) { if (exps == null || exps.isEmpty()) { return null; } if (exps.size() == 1) { return exps.iterator().next(); } return and ? new AndExpression(exps) : new OrExpression(exps); } public static Predicate> and(Collection>> exps) { return booleanExpression(exps, true); } public static Predicate> or(Collection>> exps) { return booleanExpression(exps, false); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/HasKeyExpression.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Map; public class HasKeyExpression extends SimpleExpression { HasKeyExpression(String key) { super(key); } @Override public boolean test(Map map) { return map.containsKey(key); } @Override public String toString() { return "HasKey:".concat(key); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/MatchType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import org.yx.conf.AppInfo; public final class MatchType { /** * null为false */ public static final String FALSE_BY_NULL = "null"; /** * map中没有key就为false */ public static final String FALSE_BY_NOKEY = "nokey"; /** * 只针对String,空的时候为false */ public static final String FALSE_BY_EMPTY = "empty"; public static String matchTypeOrDefault(String type) { if (type == null || (type = type.trim()).isEmpty()) { return AppInfo.get("sumk.sql.falseby", FALSE_BY_NULL); } return type; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/NotEmptyExpression.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Map; public class NotEmptyExpression extends SimpleExpression { NotEmptyExpression(String key) { super(key); } @Override public boolean test(Map map) { Object obj = map.get(key); if (obj == null) { return false; } return !"".equals(obj); } @Override public String toString() { return "NotEmpty:".concat(key); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/NotNullExpression.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Map; public class NotNullExpression extends SimpleExpression { NotNullExpression(String key) { super(key); } @Override public boolean test(Map map) { return map.get(key) != null; } @Override public String toString() { return key; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/OrExpression.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.function.Predicate; import org.yx.exception.SumkException; public class OrExpression implements Predicate> { private final Predicate>[] exps; @SuppressWarnings("unchecked") public OrExpression(Collection>> exps) { if (exps == null || exps.isEmpty()) { throw new SumkException(34554565, "ParamExpression列表不能为空"); } this.exps = exps.toArray(new Predicate[exps.size()]); } @Override public boolean test(Map map) { for (Predicate> exp : exps) { if (exp.test(map)) { return true; } } return false; } @Override public String toString() { return "OR" + Arrays.toString(exps); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/expression/SimpleExpression.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.expression; import java.util.Map; import java.util.function.Predicate; public abstract class SimpleExpression implements Predicate> { protected final String key; SimpleExpression(String key) { this.key = key; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; SimpleExpression other = (SimpleExpression) obj; if (key == null) { if (other.key != null) return false; } else if (!key.equals(other.key)) return false; return true; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/ByteArrayTypeAdapter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Base64; import org.yx.base.sumk.UnsafeByteArrayOutputStream; import org.yx.common.util.S; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; public class ByteArrayTypeAdapter extends TypeAdapter { private ByteArrayTypeAdapter() { } public static final ByteArrayTypeAdapter inst = new ByteArrayTypeAdapter(); @Override public void write(JsonWriter out, byte[] value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(new String(Base64.getEncoder().encode(value), StandardCharsets.UTF_8)); } @Override public byte[] read(JsonReader in) throws IOException { JsonToken token = in.peek(); if (token == JsonToken.NULL) { in.nextNull(); return null; } if (token == JsonToken.STRING) { String s = in.nextString(); if (s.isEmpty()) { return new byte[0]; } return S.base64().decode(s.getBytes(StandardCharsets.UTF_8)); } if (token == JsonToken.BEGIN_ARRAY) { return rawRead(in); } throw new IOException(token + " is not valid byte array token"); } private byte[] rawRead(JsonReader in) throws IOException { UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(128); in.beginArray(); while (in.hasNext()) { if (in.peek() == JsonToken.NULL) { in.nextNull(); continue; } out.write(in.nextInt()); } in.endArray(); out.flush(); out.close(); return out.toByteArray(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/GsonHelper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.util.Date; import org.yx.base.date.DateAdapters; import org.yx.base.date.DateTimeTypeAdapter; import org.yx.conf.AppInfo; import org.yx.util.StringUtil; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.LongSerializationPolicy; public final class GsonHelper { public static GsonBuilder builder(String module) { DateTimeTypeAdapter da = new DateTimeTypeAdapter(); String format = AppInfo.get("sumk.gson.date.format"); if (StringUtil.isNotEmpty(format)) { da.setDateFormat(format); } GsonBuilder gb = new GsonBuilder().registerTypeAdapter(Date.class, da); if (AppInfo.getBoolean("sumk.gson.disableHtmlEscaping", true)) { gb.disableHtmlEscaping(); } if (AppInfo.getBoolean("sumk.gson.shownull", false)) { gb.serializeNulls(); } if (AppInfo.getBoolean("sumk.gson.disableInnerClassSerialization", false)) { gb.disableInnerClassSerialization(); } if (AppInfo.getBoolean("sumk.gson.generateNonExecutableJson", false)) { gb.generateNonExecutableJson(); } if (AppInfo.getBoolean("sumk.gson.serializeSpecialFloatingPointValues", false)) { gb.serializeSpecialFloatingPointValues(); } if (AppInfo.getBoolean("sumk.gson.longSerialize2String", false)) { gb.setLongSerializationPolicy(LongSerializationPolicy.STRING); } if (AppInfo.getBoolean("sumk.gson.prettyPrinting", false)) { gb.setPrettyPrinting(); } if (AppInfo.getBoolean("sumk.gson.date.adaper", true)) { DateAdapters.registerAll(gb); } if (AppInfo.getBoolean("sumk.gson.pojo.optional", true)) { gb.registerTypeAdapterFactory(new ParamPojoTypeAdapterFactory()); } return gb; } public static Gson gson(String module) { return builder(module).create(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/GsonOperator.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.lang.reflect.Type; import java.util.Objects; import org.yx.base.sumk.UnsafeStringWriter; import com.google.gson.Gson; public class GsonOperator implements JsonOperator { private Gson gson; public GsonOperator(Gson gson) { this.gson = Objects.requireNonNull(gson); } public void setGson(Gson gson) { this.gson = Objects.requireNonNull(gson); } public Gson getGson() { return gson; } @Override public String toJson(Object obj) { UnsafeStringWriter writer = new UnsafeStringWriter(32); gson.toJson(obj, writer); return writer.toString(); } @Override public T fromJson(String json, Class clz) { return gson.fromJson(json, clz); } @Override public T fromJson(String json, Type type) { return gson.fromJson(json, type); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/JsonOperator.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.lang.reflect.Type; public interface JsonOperator { String toJson(Object obj); T fromJson(String json, Class clz); T fromJson(String json, Type type); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/JsonTypes.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.google.gson.reflect.TypeToken; public final class JsonTypes { private static final ConcurrentMap types = new ConcurrentHashMap<>(); public static Type registe(String name, Type type) { return types.put(name, type); } public static Type registeIfAbsent(String name, Type type) { return types.putIfAbsent(name, type); } public static Type remove(String name) { return types.remove(name); } public static Type get(String name) { return types.get(name); } public static Set keys() { return new HashSet<>(types.keySet()); } public static Type registe(Type type) { return types.put(type.getTypeName(), TypeToken.get(type).getType()); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/ParamPojoTypeAdapter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.io.IOException; import org.yx.annotation.doc.NotNull; import org.yx.bean.aop.asm.ParamPojo; import org.yx.bean.aop.asm.MethodPojo; import org.yx.exception.SumkException; import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; public class ParamPojoTypeAdapter extends TypeAdapter { @NotNull private final Gson gson; @NotNull private final MethodPojo info; public ParamPojoTypeAdapter(Gson gson, MethodPojo info) { this.info = info; this.gson = gson; } @Override public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } String name = null; try { T pojo = info.createEmptyParamObj(); Object[] objs = new Object[info.paramLength()]; in.beginObject(); while (in.hasNext()) { name = in.nextName(); int index = info.getIndex(name); if (index < 0) { in.skipValue(); continue; } objs[index] = gson.getAdapter(TypeToken.get(info.getParamType(index))).read(in); } in.endObject(); pojo.setParams(objs); return pojo; } catch (Exception e) { throw new SumkException(34534234, info.paramClz().getSimpleName() + "解析" + name + "字段出错:" + e.getMessage(), e); } } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public void write(JsonWriter out, ParamPojo pojo) throws IOException { if (pojo == null) { out.nullValue(); return; } out.beginObject(); Object[] objs = pojo.params(); int len = info.paramLength(); for (int i = 0; i < len; i++) { out.name(info.getParamName(i)); TypeAdapter adapter = gson.getAdapter(TypeToken.get(info.getParamType(i))); adapter.write(out, objs[i]); } out.endObject(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/ParamPojoTypeAdapterFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import org.yx.bean.aop.asm.ParamPojo; import org.yx.bean.aop.asm.ParamPojos; import org.yx.bean.aop.asm.MethodPojo; import org.yx.log.Logs; import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; public class ParamPojoTypeAdapterFactory implements TypeAdapterFactory { @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public TypeAdapter create(Gson gson, TypeToken type) { if (!ParamPojo.class.isAssignableFrom(type.getRawType())) { return null; } Class clz = (Class) type.getRawType(); MethodPojo en = ParamPojos.get((Class) clz); if (en == null) { Logs.system().info("{} cannot found ParamPojoInfo", clz.getSimpleName()); return null; } if (Logs.system().isDebugEnabled()) { Logs.system().debug("add ParamPojoTypeAdapter for {}", clz.getSimpleName()); } return new ParamPojoTypeAdapter(gson, en); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/json/ServerJsonExclusionStrategy.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.json; import java.lang.annotation.Annotation; import org.yx.annotation.ExcludeFromParams; import org.yx.annotation.ExcludeFromResponse; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import com.google.gson.GsonBuilder; public class ServerJsonExclusionStrategy implements ExclusionStrategy { private final Class annotation; public ServerJsonExclusionStrategy(Class excludeAnnotation) { this.annotation = excludeAnnotation; } @Override public boolean shouldSkipField(FieldAttributes f) { return f.getAnnotation(annotation) != null; } @Override public boolean shouldSkipClass(Class clazz) { return clazz.getAnnotation(annotation) != null; } public static GsonBuilder addServerExclusionStrategy(GsonBuilder gb) { return gb.addSerializationExclusionStrategy(new ServerJsonExclusionStrategy(ExcludeFromResponse.class)) .addDeserializationExclusionStrategy(new ServerJsonExclusionStrategy(ExcludeFromParams.class)); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/listener/ConcurrentSumkListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.listener; import java.util.List; import java.util.concurrent.Executor; import org.yx.base.context.ActionContext; import org.yx.common.util.S; public abstract class ConcurrentSumkListener implements SumkListener { private final Executor executor; public ConcurrentSumkListener() { Executor ex = this.createExecutor(); this.executor = ex == null ? S.executor() : ex; } @Override public void listenBatch(List events) throws Exception { executor.execute(ActionContext.wrapExecutable(() -> this.asyncListenBatch(events))); } @Override public void listen(Object event) throws Exception { executor.execute(ActionContext.wrapExecutable(() -> this.asyncListen(event))); } public Executor executor() { return this.executor; } /** * 创建线程池,只在初始化的时候调用一次。 如果想要单线程排队执行,使用Executors.newSingleThreadExecutor()就可以 * * @return 线程池,如果返回null就使用系统默认的线程池 */ protected Executor createExecutor() { return null; } protected void asyncListenBatch(List events) throws Exception { for (Object event : events) { this.listen(event); } } protected abstract void asyncListen(Object event) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/listener/EventBus.java ================================================ package org.yx.common.listener; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import org.yx.base.context.ActionContext; import org.yx.log.Log; import org.yx.util.CollectionUtil; /** * 它的bean统一有框架创建 */ public class EventBus { private final SumkListener[] listeners; private final Executor executor; public EventBus(List list, Executor executor) { list.sort(null); this.listeners = list.toArray(new SumkListener[list.size()]); this.executor = Objects.requireNonNull(executor); } public void publishBatch(List events) { for (SumkListener l : listeners) { try { l.listenBatch(events); } catch (Throwable e) { Log.get("sumk.event").error(l + "批量执行出错," + e.getLocalizedMessage(), e); } } } public void publish(Object event) { for (SumkListener l : listeners) { try { l.listen(event); } catch (Exception e) { Log.get("sumk.event").error(l + "执行出错," + e.getLocalizedMessage(), e); } } } public void asyncPublishBatch(List events) { executor.execute(ActionContext.wrapExecutable(() -> this.publishBatch(events))); } public void asyncPublish(Object event) { executor.execute(ActionContext.wrapExecutable(() -> this.publish(event))); } public List listeners() { return CollectionUtil.unmodifyList(listeners); } public Executor executor() { return this.executor; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/listener/EventBusFactory.java ================================================ package org.yx.common.listener; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import org.yx.annotation.Bean; import org.yx.annotation.Priority; import org.yx.bean.FactoryBean; import org.yx.bean.IOC; import org.yx.bean.NamedBean; import org.yx.log.Log; import org.yx.main.SumkServer; @Bean @Priority(20000) public class EventBusFactory implements FactoryBean { @Override public Collection beans() { Map> map = new HashMap<>(); for (SumkListener listener : IOC.getBeans(SumkListener.class)) { for (String type : listener.acceptType()) { if (type == null || (type = type.trim()).isEmpty() || IOC.get(type, EventBus.class) != null) { Log.get("sumk.listen").error("{}的EventBus已经存在", type); continue; } List list = map.get(type); if (list == null) { list = new ArrayList<>(); map.put(type, list); } if (!list.contains(listener)) { list.add(listener); } } } List beans = new ArrayList<>(); for (String type : map.keySet()) { Executor ex = SumkServer.getExecutor("sumk.event.executor." + type); EventBus bus = new EventBus(map.get(type), ex); beans.add(new NamedBean(type, bus)); } return beans; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/listener/SumkListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.listener; import java.util.Collection; import java.util.List; import org.yx.base.Ordered; /** * 异步执行使用ConcurrentSumkListener */ public interface SumkListener extends Ordered { Collection acceptType(); default void listenBatch(List events) throws Exception { for (Object event : events) { this.listen(event); } } void listen(Object event) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/locale/I18n.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.locale; import java.util.Locale; import java.util.Objects; /** * 一般用于国际化或提示语 */ public final class I18n { private static I18nMessageProvider provider = new I18nMessageProviderImpl(); static I18nMessageProvider getProvider() { return provider; } static void setProvider(I18nMessageProvider provider) { I18n.provider = Objects.requireNonNull(provider); } public static String get(String orignName, String defaultTemplate, Object... params) { return provider.get(orignName, defaultTemplate, params); } public static String getInLocale(Locale locale, String orignName, String defaultTemplate, Object... params) { return provider.getInLocale(locale, orignName, defaultTemplate, params); } public static void setCurrentLocale(Locale locale) { provider.setCurrentLocale(locale); } public static void clearCurrentLocale() { provider.clearCurrentLocale(); } public static Locale getCurrentLocale() { return provider.getCurrentLocale(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/locale/I18nBuilder.java ================================================ package org.yx.common.locale; public class I18nBuilder { public static I18nMessageProvider getProvider() { return I18n.getProvider(); } static void setProvider(I18nMessageProvider provider) { I18n.setProvider(provider); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/locale/I18nMessageProvider.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.locale; import java.util.Locale; public interface I18nMessageProvider { void setCurrentLocale(Locale locale); void clearCurrentLocale(); Locale getCurrentLocale(); String get(String orignName, String defaultTemplate, Object... params); String getInLocale(Locale locale, String orignName, String defaultTemplate, Object... params); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/locale/I18nMessageProviderImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.locale; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import org.yx.conf.AppInfo; import org.yx.util.StringUtil; /** * 一般用于国际化或提示语 */ public class I18nMessageProviderImpl implements I18nMessageProvider { private static final ThreadLocal CURRENT_LOCALE = new ThreadLocal<>(); @Override public String get(String orignName, String defaultTemplate, Object... params) { Locale locale = this.getCurrentLocale(); if (locale == null) { locale = Locale.getDefault(); } return this.getInLocale(locale, orignName, defaultTemplate, params); } @Override public String getInLocale(Locale locale, String orignName, String defaultTemplate, Object... params) { List names = localedNames(orignName, locale); String template = null; for (String localeName : names) { template = AppInfo.get(localeName); if (StringUtil.isNotEmpty(template)) { return buildMessage(template, locale, params); } } return buildMessage(defaultTemplate, locale, params); } public List localedNames(String name, Locale locale) { if (locale == null) { return Collections.singletonList(name); } List result = new ArrayList<>(3); result.add(name); String language = locale.getLanguage(); String country = locale.getCountry(); String variant = locale.getVariant(); StringBuilder temp = new StringBuilder(); if (language.length() > 0) { temp.append(language); result.add(0, buildName(name, temp)); } if (country.length() > 0) { if (temp.length() > 0) { temp.append('_'); } temp.append(country); result.add(0, buildName(name, temp)); } if (variant.length() > 0 && (language.length() > 0 || country.length() > 0)) { if (temp.length() > 0) { temp.append('_'); } temp.append(variant); result.add(0, buildName(name, temp)); } return result; } protected String buildName(String name, StringBuilder temp) { StringBuilder sb = new StringBuilder(temp); if (sb.length() > 0) { sb.append('.'); } return sb.append(name).toString(); } protected String buildMessage(String template, Locale locale, Object... params) { MessageFormat formater = new MessageFormat(template); if (locale != null) { formater.setLocale(locale); } return formater.format(params); } @Override public void setCurrentLocale(Locale locale) { CURRENT_LOCALE.set(locale); } @Override public void clearCurrentLocale() { CURRENT_LOCALE.remove(); } @Override public Locale getCurrentLocale() { return CURRENT_LOCALE.get(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/lock/Lock.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.lock; /** * 分布式锁的钥匙。lock方法要从S.lock进入 * * @author 游夏 * */ public interface Lock extends AutoCloseable { String getId(); String getValue(); void unlock(); @Override default void close() { this.unlock(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/lock/Locked.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.lock; public final class Locked implements Lock { private final String id; private final String value; public Locked(String id, String value) { this.id = id; this.value = value; } @Override public String getId() { return this.id; } @Override public String getValue() { return value; } @Override public void unlock() { } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/lock/Locker.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.lock; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Objects; import java.util.Set; import org.yx.base.context.AppContext; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.main.SumkServer; import org.yx.redis.Redis; import org.yx.redis.RedisPool; import org.yx.util.IOUtil; import org.yx.util.Task; public final class Locker { private static final int REDIS_LEN = 16; private static final String[] nodeKey = new String[REDIS_LEN]; public static synchronized void init() { if (nodeKey[0] != null) { return; } for (int i = 0; i < REDIS_LEN; i++) { nodeKey[i] = "lock_" + i; } ensureScriptRunner.run(); Task.scheduleAtFixedRate(ensureScriptRunner, AppInfo.getLong("sumk.lock.schedule.delay", 1000L * 600), AppInfo.getLong("sumk.lock.schedule.delay", 1000L * 600)); } private Locker() { } public static final Locker inst = new Locker(); static Redis redis(String id) { int index = id.hashCode() & (REDIS_LEN - 1); Redis redis = RedisPool.get(nodeKey[index]); if (redis == null) { throw new SumkException(8295462, "SLock must use in redis environment"); } return redis.mute(); } private static final ThreadLocal> locks = new ThreadLocal>() { @Override protected List initialValue() { return new ArrayList<>(2); } }; private static SLock getLock(final Lock key) { List list = locks.get(); if (list == null || list.isEmpty() || key == null) { return null; } list = new ArrayList<>(list); for (SLock lock : list) { if (!lock.isEnable()) { Log.get("sumk.lock").warn("remove unable lock: {}", lock); lock.unlock(); continue; } if (lock.getId().equals(key.getId())) { return lock; } } return null; } public int releaseLocalLocks() { List list = locks.get(); list = new ArrayList<>(list); for (SLock lock : list) { lock.unlock(); } locks.remove(); return list.size(); } void remove(final Lock lock) { if (lock == null || lock instanceof Locked) { return; } List list = locks.get(); if (list == null || list.isEmpty()) { return; } ListIterator it = list.listIterator(list.size()); while (it.hasPrevious()) { SLock lock2 = it.previous(); if (lock2.getId().equals(lock.getId())) { it.remove(); } } } /** * * @param name 要被锁的对象 * @param maxWaitTime 获取锁的最大时间,单位ms * @param maxLockTime 最大的锁住时间,单位ms * @return Key对象,或者null */ public Lock tryLock(String name, int maxWaitTime, int maxLockTime) { SLock lock = SLock.create(name, maxLockTime); return tryLock(lock, maxWaitTime); } /** * * @param name 要被锁的对象 * @param maxWaitTime 获取锁的最大时间,单位ms。如果只想尝试一次,可以传0 * @return 锁的钥匙,获取不到锁就返回null */ public Lock tryLock(String name, int maxWaitTime) { SLock lock = SLock.create(name); return tryLock(lock, maxWaitTime); } public Lock lock(String name) { return tryLock(name, Integer.MAX_VALUE); } /** * 尝试加锁,如果锁定失败,就返回null * * @param lock 锁对象 * @param maxWaitTime 获取锁的最大时间,单位ms。无论值为多少,都至少会尝试一次 * @return Key对象,或者null */ public Lock tryLock(SLock lock, int maxWaitTime) { Lock old = getLock(lock); if (old != null) { return new Locked(old.getId(), old.getValue()); } if (lock.lock(maxWaitTime)) { locks.get().add(lock); return lock; } return null; } public boolean isLockedNow(Lock lock) { return Objects.equals(lock.getValue(), Locker.redis(lock.getId()).get(lock.getId())); } /** * 重置锁超时时间,对重进入的锁也适用 * * @param lock 锁 * @param mils 重置锁的过期时间为当前值 * @return 如果锁已经被释放,或其它未知原因,就返回false */ public boolean resetExpiredTime(Lock lock, int mils) { SLock slock = getLock(lock); if (slock == null || !isLockedNow(slock)) { return false; } long endTime = System.currentTimeMillis() + mils; Long resp = Locker.redis(slock.getId()).pexpireAt(slock.getId(), endTime); if (resp != null && resp.longValue() > 0) { slock.resetEndTime(endTime); return true; } return false; } private static final Runnable ensureScriptRunner = () -> { try { InputStream in = SumkServer.class.getClassLoader().getResourceAsStream("META-INF/lua_del"); String script = new String(IOUtil.readAllBytes(in, true), AppInfo.UTF8); script = script.replace('\r', '\n').replace("\n\n", "\n"); Set set = new HashSet<>(); for (String key : nodeKey) { Redis redis = RedisPool.get(key); if (redis == null || !set.add(redis)) { continue; } Log.get("sumk.lock").debug("init lock script", redis); redis.scriptLoad(script); } } catch (Exception e) { Log.get("sumk.lock").error("Lock init failed. Maybe you need restart!!!"); Log.printStack("sumk.lock", e); if (AppInfo.getBoolean("sumk.shutdown.if.lock.failed", false)) { AppContext.startFailed(); } } }; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/lock/SLock.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.lock; import org.yx.common.util.kit.Asserts; import org.yx.conf.AppInfo; import org.yx.log.Log; import org.yx.util.UUIDSeed; public final class SLock implements Lock { private static final String SHA = "fc8341f94e518c9868148c2b8fc7cef25ec6fa85"; private static final long CLOSED = -1; private final String id; private final String value; private final int maxLockTime; private final int intervalTime; private long endTime; private long startNS; public SLock(String keyId, String value, int maxLockTime, int intervalTime) { Asserts.requireTrue(keyId != null && (keyId = keyId.trim()).length() > 0, "lock name cannot be empty"); Asserts.requireTrue(intervalTime > 0 && maxLockTime > 0 && value != null && value.length() > 0, "lock param is not valid"); this.id = keyId; this.value = value; this.maxLockTime = maxLockTime; this.intervalTime = intervalTime; } public String getId() { return id; } public String getValue() { return value; } public static SLock create(String name, int maxLockTime, int intervalTime) { return new SLock(name, UUIDSeed.seq(), maxLockTime, intervalTime); } public static SLock create(String name) { return create(name, AppInfo.getInt("sumk.lock.maxLockTime", 60000)); } public static SLock create(String name, int maxLockTime) { return create(name, maxLockTime, AppInfo.getInt("sumk.lock.intervalTime", 50)); } boolean tryLock() { this.startNS = System.nanoTime(); String ret = Locker.redis(id).set(id, value, "NX", "PX", maxLockTime); if (ret == null) { return false; } return ret.equalsIgnoreCase("OK") || ret.equals("1"); } boolean lock(final int maxWaitTime) { long now = System.currentTimeMillis(); this.endTime = now + this.maxLockTime; if (this.endTime < 1) { this.endTime = 1; } long waitEndTime = now + maxWaitTime; while (true) { if (tryLock()) { return true; } long left = waitEndTime - System.currentTimeMillis(); if (left <= 0) { return false; } long sleepTime = Math.min(left, (long) this.intervalTime); try { Log.get("sumk.lock").debug("locked failed: {}={},sleep {}ms", id, value, sleepTime); Thread.sleep(sleepTime); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } } void resetEndTime(long endTime) { this.endTime = endTime; } boolean isEnable() { long end = this.endTime; return end > 0 && System.currentTimeMillis() < end; } @Override public void unlock() { if (this.endTime == CLOSED) { return; } Locker.redis(id).evalsha(SHA, 1, id, value); this.endTime = CLOSED; Locker.inst.remove(this); Log.get("sumk.lock").info("unlock {}:{},spent {}ns", id, value, System.nanoTime() - this.startNS); } @Override public String toString() { return id + "=" + value + " : " + this.endTime; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/monitor/MessageProvider.java ================================================ package org.yx.common.monitor; public interface MessageProvider { Object get(String type, String key, Object param); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/monitor/Monitors.java ================================================ package org.yx.common.monitor; import java.util.ArrayList; import java.util.List; public class Monitors { private static final List providers = new ArrayList<>(5); public static final String BLANK = " "; public static synchronized void add(MessageProvider provider) { if (!providers.contains(provider)) { providers.add(provider); } } public static Object getMessage(String type, String key, Object param) { for (MessageProvider p : providers) { Object msg = p.get(type, key, param); if (msg != null) { return msg; } } return null; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/AbstractWeightedServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; import java.util.Objects; public abstract class AbstractWeightedServer implements WeightedServer { protected final T source; private int weight = 1; public AbstractWeightedServer(T source) { this.source = Objects.requireNonNull(source); } @Override public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight > 0 ? weight : 0; } @Override public T getSource() { return this.source; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((source == null) ? 0 : source.hashCode()); result = prime * result + weight; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AbstractWeightedServer other = (AbstractWeightedServer) obj; if (source == null) { if (other.source != null) return false; } else if (!source.equals(other.source)) return false; if (weight != other.weight) return false; return true; } @Override public int compareTo(WeightedServer o) { return Integer.compare(o.getWeight(), this.getWeight()); } @Override public String toString() { return "WeightedSource [source=" + source + ", weight=" + weight + "]"; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/EmptyRouter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; import java.util.Collections; import java.util.List; public class EmptyRouter implements Router { @Override public T select() { return null; } @Override public List allSources() { return Collections.emptyList(); } @Override public List aliveSources() { return Collections.emptyList(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/Router.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; import java.util.List; public interface Router { T select(); List allSources(); List aliveSources(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/Routes.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; import java.util.Collection; public final class Routes { public static Router createWeightedRouter(Collection> servers) { if (servers == null || servers.isEmpty()) { return new EmptyRouter<>(); } if (servers.size() == 1) { return new SingleRouter<>(servers.iterator().next()); } return new WeightedRouter<>(servers); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/SingleRouter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; import java.util.Collections; import java.util.List; import java.util.Objects; public class SingleRouter implements Router { private final WeightedServer server; public SingleRouter(WeightedServer server) { this.server = Objects.requireNonNull(server); } @Override public T select() { if (!this.server.isEnable()) { return null; } return this.server.getSource(); } @Override public List allSources() { return Collections.singletonList(this.server.getSource()); } @Override public List aliveSources() { return server.isEnable() ? Collections.singletonList(this.server.getSource()) : Collections.emptyList(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/WeightedRouter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; public class WeightedRouter implements Router { protected int currentIndex = -1; protected int currentWeight; protected final int MAX_WEIGHT; protected final int GCD_WEIGHT; protected final WeightedServer[] SERVERS; public WeightedRouter(Collection> servers) { @SuppressWarnings("unchecked") WeightedServer[] ws = servers.toArray(new WeightedServer[servers.size()]); Arrays.sort(ws); SERVERS = ws; MAX_WEIGHT = getMaxWeightForServers(); GCD_WEIGHT = getGCDForServers(); this.currentWeight = this.MAX_WEIGHT; } private BigInteger gcd(BigInteger a, BigInteger b) { return a.gcd(b); } protected int getGCDForServers() { List list = new ArrayList<>(SERVERS.length); for (WeightedServer sm : SERVERS) { int weight = sm.getWeight(); if (weight <= 0) { continue; } list.add(new BigInteger(String.valueOf(weight))); } BigInteger w = new BigInteger("0"); for (int i = 0, len = list.size(); i < len - 1; i++) { if (w.intValue() == 0) { w = gcd(list.get(i), list.get(i + 1)); } else { w = gcd(w, list.get(i + 1)); } } int gcd = w.intValue(); return gcd > 0 ? gcd : 1; } protected int getMaxWeightForServers() { int w = 0; for (WeightedServer s : SERVERS) { if (s.getWeight() > w) { w = s.getWeight(); } } return w; } @Override public List allSources() { List list = new ArrayList<>(SERVERS.length); for (WeightedServer s : this.SERVERS) { list.add(s.getSource()); } return list; } @Override public List aliveSources() { List list = new ArrayList<>(SERVERS.length); for (WeightedServer s : this.SERVERS) { if (!s.isEnable()) { continue; } list.add(s.getSource()); } return list; } @Override public T select() { int index = 0; final int SERVER_COUNT = SERVERS.length; for (int i = 0; i < SERVER_COUNT; i++) { index = (currentIndex + 1) % SERVER_COUNT; currentIndex = index; if (index == 0) { int tempWeight = currentWeight - GCD_WEIGHT; currentWeight = tempWeight < 1 ? MAX_WEIGHT : tempWeight; } WeightedServer server = SERVERS[index]; if (server.getWeight() >= currentWeight) { if (!server.isEnable()) { continue; } return server.getSource(); } } for (int i = 0; i < SERVER_COUNT; i++) { WeightedServer w = SERVERS[i % SERVER_COUNT]; if (w.isEnable()) { return w.getSource(); } } return null; } @Override public String toString() { return "[MAX_WEIGHT=" + MAX_WEIGHT + ", GCD_WEIGHT=" + GCD_WEIGHT + ", SERVER_COUNT=" + SERVERS.length + "]"; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/route/WeightedServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.route; public interface WeightedServer extends Comparable> { int getWeight(); void setWeight(int weight); T getSource(); boolean isEnable(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/AbstractSeq.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicIntegerArray; import org.slf4j.Logger; import org.yx.log.Log; public abstract class AbstractSeq implements Seq { public static final long FROMMILS_V1 = 1420041600000L; public static final long FROMMILS_V2 = 1717171200000L; private static final int LOCAL_SEQ_INDEX = 64; private AtomicIntegerArray localSeqs = new AtomicIntegerArray(LOCAL_SEQ_INDEX + 1); protected final long fromMils; protected SeqCounter counter; public AbstractSeq() { this(FROMMILS_V2); } public AbstractSeq(long from) { this.fromMils = from; try { for (int i = 0; i < localSeqs.length(); i++) { localSeqs.set(i, ThreadLocalRandom.current().nextInt(256)); } } catch (Exception e) { Log.get("sumk.seq").error(e.getLocalizedMessage(), e); } } protected int localHashIndex(String name) { if (name == null || name.isEmpty()) { return LOCAL_SEQ_INDEX; } return name.hashCode() & (LOCAL_SEQ_INDEX - 1); } protected int localSeq(String name) { int hash = localHashIndex(name); int num = localSeqs.incrementAndGet(hash); if (num > 0x3FFFFFFF) { localSeqs.compareAndSet(hash, num, ThreadLocalRandom.current().nextInt(100)); } return num; } protected int subNumber(String name) { if (counter != null) { try { return counter.incr(name); } catch (Exception e) { Logger log = Log.get("sumk.seq"); if (log.isTraceEnabled()) { log.trace(e.getLocalizedMessage(), e); } else { log.debug(e.toString()); } } } int sub = (ThreadLocalRandom.current().nextInt(0x100) << 16); sub |= ((int) System.nanoTime()) & 0xFF00; return sub | (localSeq(name) & 0xFF); } @Override public void setCounter(SeqCounter counter) { this.counter = counter; } @Override public SeqCounter getCounter() { return this.counter; } protected final long shortMills(long time) { return time - fromMils; } protected long fullTime(long time) { return time + fromMils; } @Override public long next() { return next(null); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/LongTermSeqImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; import org.yx.util.SumkDate; public final class LongTermSeqImpl extends AbstractSeq { public LongTermSeqImpl() { super(); } public LongTermSeqImpl(long from) { super(from); } public long next(String name) { long sub = subNumber(name) & 0x7FFFFFL; return prefix(System.currentTimeMillis()) | sub; } private long prefix(long time) { long num = shortMills(time); num &= 0x1FFFFFFFFFFL; return num << 23; } public long getTimeMillis(long seq) { long num = seq & 0xFFFFFFFFFF800000L; num >>>= 23; return fullTime(num); } @Override public long low(SumkDate date) { return prefix(date.getTimeInMils()); } @Override public long high(SumkDate date) { return prefix(date.getTimeInMils()) | 0x7FFFFF; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/Seq.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; import org.yx.util.SumkDate; public interface Seq { void setCounter(SeqCounter counter); SeqCounter getCounter(); long next(String name); long next(); long getTimeMillis(long seq); long low(SumkDate date); long high(SumkDate date); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/SeqCounter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; public interface SeqCounter { int incr(String name) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/SeqHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; import java.util.Objects; import org.yx.conf.AppInfo; import org.yx.log.Log; public class SeqHolder { private static Seq createDefaultSeq() { int snow = AppInfo.getInt("sumk.seq.counter.snow", Integer.MIN_VALUE); if (snow != Integer.MIN_VALUE) { Seq seq = new SeqImpl(); seq.setCounter(new SnowflakeCounter(snow)); Log.get("sumk.seq").info("use snow counter"); return seq; } int version = AppInfo.getInt("sumk.seq.version", 0); if (version == 1) { Log.get("sumk.seq").info("use v1.0 seq"); return new SeqImpl(AbstractSeq.FROMMILS_V1); } return new LongTermSeqImpl(); } private static Seq inst = createDefaultSeq(); public static Seq inst() { return inst; } public static void setSeq(Seq seq) { SeqHolder.inst = Objects.requireNonNull(seq); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/SeqImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; import org.yx.util.SumkDate; public final class SeqImpl extends AbstractSeq { public SeqImpl() { super(); } public SeqImpl(long from) { super(from); } @Override public long next(String name) { int sub = subNumber(name) & 0xFFFFFF; return prefix(System.currentTimeMillis()) | sub; } private long prefix(long time) { long num = shortMills(time); num &= 0xFFFFFFFFFFL; return num << 24; } @Override public long getTimeMillis(long seq) { long num = seq & 0xFFFFFFFFFF000000L; num >>>= 24; return fullTime(num); } @Override public long low(SumkDate date) { return prefix(date.getTimeInMils()); } @Override public long high(SumkDate date) { return prefix(date.getTimeInMils()) | 0xFFFFFF; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/sequence/SnowflakeCounter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.sequence; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; public class SnowflakeCounter implements SeqCounter { private final int snow; private final AtomicInteger seq; @Override public int incr(String name) throws Exception { int random = seq.incrementAndGet() & 0xFFFF; random <<= 8; return snow | random; } public SnowflakeCounter(int snow) { this.snow = snow & 0xFF; seq = new AtomicInteger(ThreadLocalRandom.current().nextInt()); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/S.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util; import org.yx.base.thread.SumkExecutorService; import org.yx.common.json.GsonHelper; import org.yx.common.lock.Locker; import org.yx.common.util.kit.BeanConverter; import org.yx.common.util.secury.AESEncryptor; import org.yx.common.util.secury.Base64; import org.yx.common.util.secury.Base64Impl; import org.yx.common.util.secury.CommonDigest; import org.yx.common.util.secury.Encryptor; import org.yx.common.util.secury.Hasher; import org.yx.util.SumkThreadPool; import com.google.gson.Gson; public final class S { private static Gson json = GsonHelper.gson("sumk"); private static SumkExecutorService executor = SumkThreadPool.executor(); private static Base64 base64 = Base64Impl.inst; private static Encryptor cipher = new AESEncryptor(); private static Hasher hash = new CommonDigest("SHA-256"); private static Locker lock = Locker.inst; private static BeanConverter bean = new BeanConverter(); /** * @return json工具 */ public static Gson json() { return json; } /** * @return 系统共用的线程池 */ public static SumkExecutorService executor() { return executor; } public static Base64 base64() { return base64; } /** * @return 加密器,默认是AES对称加密 */ public static Encryptor cipher() { return cipher; } /** * @return hash工具,就是大家常说的md5 */ public static Hasher hash() { return hash; } /** * @return 分布式锁 */ public static Locker lock() { return lock; } /** * @return bean和map的转换,以及属性复制。只支持第一级field */ public static BeanConverter bean() { return bean; } static void setJson(Gson json) { S.json = json; } static void setExecutor(SumkExecutorService executor) { S.executor = executor; } static void setBase64(Base64 base64) { S.base64 = base64; } static void setCipher(Encryptor cipher) { S.cipher = cipher; } static void setHash(Hasher hash) { S.hash = hash; } static void setLock(Locker lock) { S.lock = lock; } static void setBean(BeanConverter bean) { S.bean = bean; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/SBuilder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.yx.base.thread.SumkExecutorService; import org.yx.common.lock.Locker; import org.yx.common.util.kit.BeanConverter; import org.yx.common.util.secury.Base64; import org.yx.common.util.secury.Encryptor; import org.yx.common.util.secury.Hasher; import com.google.gson.Gson; public final class SBuilder { public static class MapBuilder { private final Map map; public MapBuilder(Map map) { this.map = map; } public MapBuilder put(K key, V value) { map.put(key, value); return this; } public MapBuilder remove(K key) { map.remove(key); return this; } public Map toMap() { return this.map; } } public static MapBuilder map(Class valueType) { return new MapBuilder<>(new HashMap()); } public static MapBuilder map(Map map) { return new MapBuilder<>(map); } public static MapBuilder map() { return new MapBuilder<>(new HashMap()); } public static MapBuilder map(String key, Object value) { return map().put(key, value); } public static void setJson(Gson json) { S.setJson(Objects.requireNonNull(json)); } public static void setExecutor(SumkExecutorService executor) { S.setExecutor(Objects.requireNonNull(executor)); } public static void setBase64(Base64 base64) { S.setBase64(Objects.requireNonNull(base64)); } public static void setCipher(Encryptor cipher) { S.setCipher(Objects.requireNonNull(cipher)); } public static void setHash(Hasher hash) { S.setHash(Objects.requireNonNull(hash)); } public static void setLock(Locker lock) { S.setLock(Objects.requireNonNull(lock)); } public static void setBean(BeanConverter bean) { S.setBean(Objects.requireNonNull(bean)); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/SeqUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util; import org.yx.common.sequence.SeqHolder; import org.yx.util.SumkDate; public final class SeqUtil { public static long next() { return SeqHolder.inst().next(); } public static long next(String name) { return SeqHolder.inst().next(name); } public static String nextString() { return Long.toString(next(), Character.MAX_RADIX); } public static String nextString(String name) { return Long.toString(next(name), Character.MAX_RADIX); } public static long getTimeMillis(long seq) { return SeqHolder.inst().getTimeMillis(seq); } public static SumkDate toSumkDate(long seq) { return SumkDate.of(getTimeMillis(seq)); } public static long from(SumkDate date) { return SeqHolder.inst().low(date); } public static long to(SumkDate date) { return SeqHolder.inst().high(date); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/helper/ArrayHelper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.helper; import java.lang.reflect.Array; import java.util.Objects; public final class ArrayHelper { public static T[] add(T[] old, T obj, Class clz) { if (obj == null) { return old; } if (old == null || old.length == 0) { T[] ret = createArray(clz, 1); ret[0] = obj; return ret; } for (T f : old) { if (f.equals(obj)) { return old; } } T[] ret = createArray(clz, old.length + 1); System.arraycopy(old, 0, ret, 0, old.length); ret[old.length] = obj; return ret; } public static T[] remove(T[] old, T obj, Class clz) { if (old == null || old.length == 0 || obj == null) { return old; } for (int i = 0; i < old.length; i++) { T f = old[i]; if (Objects.equals(f, obj)) { T[] ret = createArray(clz, old.length - 1); System.arraycopy(old, 0, ret, 0, i); System.arraycopy(old, i + 1, ret, i, ret.length - i); return ret; } } return old; } @SuppressWarnings("unchecked") private static T[] createArray(Class clz, int length) { return (T[]) Array.newInstance(clz, length); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/kit/Asserts.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.kit; import org.yx.exception.SumkException; public final class Asserts { public static void requireTrue(boolean b, String msg) { if (b) { return; } throw new SumkException(5674354, msg); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/kit/BeanConverter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.kit; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import org.yx.exception.SumkException; import org.yx.util.Loader; /** * 支持子类,但不支持属性的嵌套解析 */ public class BeanConverter { private static final String JAVA_PRE = "java"; private Map, Field[]> cache = Collections .synchronizedMap(new LinkedHashMap, Field[]>(32, 0.75f, true) { private static final long serialVersionUID = 1L; @Override protected boolean removeEldestEntry(Entry, Field[]> eldest) { return this.size() > 800; } }); private Field[] parseFields(Class clz) { List list = new ArrayList<>(); Class tempClz = clz; while (tempClz != null && tempClz != Object.class) { Field[] fs = cache.get(tempClz); if (fs != null) { Collections.addAll(list, fs); break; } fs = tempClz.getDeclaredFields(); for (Field f : fs) { if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT | Modifier.FINAL)) != 0) { continue; } if (!f.isAccessible()) { f.setAccessible(true); } list.add(f); } tempClz = tempClz.getSuperclass(); } return list.toArray(new Field[list.size()]); } private Field[] getFieldsAndCache(Class clz) { Field[] fields = getFields(clz); Field[] f2 = cache.putIfAbsent(clz, fields); return f2 != null ? f2 : fields; } public Field[] getFields(Class clz) { Field[] fields = cache.get(clz); if (fields != null) { return fields; } fields = this.parseFields(clz); String clzName = clz.getName(); if (clzName.startsWith(JAVA_PRE) || clzName.contains("$")) { return fields; } return fields; } /** * 根据field转为map。不会对属性内部的字段再做解析 * * @param bean 用于转化的pojo对象。 * @param keepNull 如果为true,那么它就会保留null字段 * @return 返回map对象,不为null。 */ @SuppressWarnings("unchecked") public Map beanToMap(Object bean, boolean keepNull) { if (bean == null) { return Collections.emptyMap(); } if (bean instanceof Map) { return (Map) bean; } try { Field[] fields = getFieldsAndCache(bean.getClass()); Map map = new HashMap<>(); for (Field f : fields) { Object v = f.get(bean); if (v == null && !keepNull) { continue; } map.putIfAbsent(f.getName(), v); } return map; } catch (Exception e) { throw new SumkException(35432540, "beanToMap to map failed, because of " + e.getMessage(), e); } } /** * 根据map的内容填充bean的属性。只转义第一层的key。
* 对大小写和下划线不敏感 * * @param 参数类型 * @param map 原始map * @param bean 目标对象,它的属性会被填充进来 * @return 返回目标对象 */ @SuppressWarnings("unchecked") public T fillBeanIgnoreCaseAndUnderLine(Map map, T bean) { if (map == null || bean == null || map.isEmpty()) { return bean; } if (bean instanceof Map) { ((Map) bean).putAll(map); return bean; } Map tmp = new HashMap<>(); for (Entry entry : map.entrySet()) { String k = entry.getKey(); Object v = entry.getValue(); if (k == null || v == null) { continue; } tmp.put(k.toLowerCase().replace("_", ""), v); } map = tmp; Field[] fields = getFieldsAndCache(bean.getClass()); try { for (Field f : fields) { String fName = f.getName().toLowerCase().replace("_", ""); if (map.containsKey(fName)) { f.set(bean, TypeConverter.convert(map.get(fName), f.getType())); } } } catch (Exception e) { throw new SumkException(35432541, "fillBean failed, because of " + e.getMessage(), e); } return bean; } /** * 根据map的内容填充bean的属性。只转义第一层的key。 * * @param 参数类型 * @param map 原始map * @param bean 目标对象,它的属性会被填充进来 * @return 返回目标对象 */ @SuppressWarnings("unchecked") public T fillBean(Map map, T bean) { if (map == null || bean == null || map.isEmpty()) { return bean; } if (bean instanceof Map) { ((Map) bean).putAll(map); return bean; } Field[] fields = getFieldsAndCache(bean.getClass()); try { for (Field f : fields) { if (map.containsKey(f.getName())) { f.set(bean, TypeConverter.convert(map.get(f.getName()), f.getType())); } } } catch (Exception e) { throw new SumkException(35432541, "fillBean failed, because of " + e.getMessage(), e); } return bean; } public T copyFields(Object src, T dest) { if (src == null || dest == null) { return dest; } Class srcClz = src.getClass(); if (!srcClz.isInstance(dest)) { Map map = this.beanToMap(src, false); return this.fillBean(map, dest); } Field[] fields = getFieldsAndCache(srcClz); try { for (Field f : fields) { f.set(dest, f.get(src)); } } catch (Exception e) { throw new SumkException(35432542, "copyFields failed, because of " + e.getMessage(), e); } return dest; } public Object clone(Object src) { if (src == null) { return null; } Object dest; try { dest = Loader.newInstance(src.getClass()); } catch (Exception e) { throw new SumkException(35432545, "clone failed, because of " + e.getMessage(), e); } return this.copyFields(src, dest); } public Map, Field[]> getCache() { return cache; } public void setCache(Map, Field[]> threadSaftMap) { this.cache = Objects.requireNonNull(threadSaftMap); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/kit/PriorityKits.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.kit; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import org.yx.annotation.Priority; import org.yx.base.Ordered; public class PriorityKits { private static final Comparator> priorityComparator = (a, b) -> { int pa = getPriority(a); int pb = getPriority(b); if (pa == pb) { return a.getName().compareTo(b.getName()); } return Integer.compare(pa, pb); }; public static int getPriority(Class clz) { Priority p = clz.getAnnotation(Priority.class); if (p == null) { return Ordered.DEFAULT_ORDER; } return p.value(); } public static List> sort(Collection> source) { List> list = new ArrayList<>(source); list.sort(priorityComparator); return list; } /** * * @param sortedClasses 已排序好的列表 * @return 会有3个item,第一个是Priority小于默认值的,第二个是等于默认值或者没有@Priority注解,第三个Priority大于默认值的 */ public static List>> split(List> sortedClasses) { List> low = new ArrayList<>(); List> high = new ArrayList<>(); List> middle = new ArrayList<>(); final int size = sortedClasses.size(); for (int i = 0; i < size; i++) { Class clz = sortedClasses.get(i); int p = getPriority(clz); if (p == Ordered.DEFAULT_ORDER) { middle.add(clz); } else if (p < Ordered.DEFAULT_ORDER) { low.add(clz); } else { high.add(clz); } } return Arrays.asList(low, middle, high); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/kit/TypeConverter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.kit; import java.math.BigDecimal; import java.sql.Blob; import java.sql.Clob; import java.util.Objects; import java.util.function.BiFunction; import org.yx.base.date.TimeUtil; import org.yx.exception.SumkException; import org.yx.util.IOUtil; public class TypeConverter { private static BiFunction, Object> customConverter = (value, type) -> value; public static BiFunction, Object> getCustomConverter() { return customConverter; } public static void setCustomConverter(BiFunction, Object> customConverter) { TypeConverter.customConverter = Objects.requireNonNull(customConverter); } @SuppressWarnings("unchecked") public static T convert(Object value, Class type) throws Exception { if (value == null || type == value.getClass()) { return (T) value; } if (TimeUtil.isGenericDate(type)) { return TimeUtil.toType(value, type, false); } if (type.isInstance(value)) { return type.cast(value); } if (Number.class.isAssignableFrom(type) && value instanceof Number) { T t = (T) toType((Number) value, type, false); if (t != null) { return t; } } if (type == byte[].class && value instanceof Blob) { Blob v = (Blob) value; return (T) IOUtil.readAllBytes(v.getBinaryStream(), true); } if (type == String.class && value instanceof Clob) { Clob v = (Clob) value; return (T) IOUtil.readAll(v.getCharacterStream(), true); } return (T) customConverter.apply(value, type); } @SuppressWarnings("unchecked") public static T toType(Number v, Class type, boolean failIfNotSupport) { if (v.getClass() == type) { return (T) v; } if (Integer.class == type || int.class == type) { return (T) Integer.valueOf(v.intValue()); } if (Long.class == type || long.class == type) { return (T) Long.valueOf(v.longValue()); } if (Double.class == type || double.class == type) { return (T) Double.valueOf(v.doubleValue()); } if (Byte.class == type || byte.class == type) { return (T) Byte.valueOf(v.byteValue()); } if (Short.class == type || short.class == type) { return (T) Short.valueOf(v.shortValue()); } if (Float.class == type || float.class == type) { return (T) Float.valueOf(v.floatValue()); } if (BigDecimal.class == type) { if (v instanceof Integer || v instanceof Long || v instanceof Short || v instanceof Byte) { return (T) new BigDecimal(v.longValue()); } return (T) new BigDecimal(v.doubleValue()); } if (failIfNotSupport || !Number.class.isAssignableFrom(type)) { throw new SumkException(927816546, type.getClass().getName() + "is not valid Number type"); } return null; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/secury/AESEncryptor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.secury; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.yx.log.Logs; public class AESEncryptor implements Encryptor { private static final String AES = "AES"; private String c = "AES/CBC/PKCS5Padding"; public AESEncryptor() { } public AESEncryptor(String cipher) { if (cipher != null && cipher.length() > 1) { this.c = cipher; Logs.system().info("aes cipher:{}", this.c); } } public String getCipherTransformation() { return c; } protected Cipher getCipher(int mode, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance(c); if (c.contains("ECB")) { cipher.init(mode, new SecretKeySpec(key, AES)); } else { cipher.init(mode, new SecretKeySpec(key, AES), new IvParameterSpec(key)); } return cipher; } @Override public byte[] encrypt(byte[] contentBytes, byte[] key) throws Exception { if (contentBytes == null || contentBytes.length == 0) { return contentBytes; } return getCipher(Cipher.ENCRYPT_MODE, key).doFinal(contentBytes); } @Override public byte[] decrypt(byte[] contentBytes, byte[] key) throws Exception { if (contentBytes == null || contentBytes.length == 0) { return contentBytes; } return getCipher(Cipher.DECRYPT_MODE, key).doFinal(contentBytes); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/secury/Base64.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.secury; public interface Base64 { /** * 解码,是否含有\r\n都能解码 * * @param src 密文 * @return 明文 */ byte[] decode(byte[] src); /** * 解码,是否含有\r\n都能解码 * * @param src 密文 * @return 明文 */ byte[] decode(String src); /** * 使用标准方式进行编码,不含有换行符 * * @param src 原文 * @return 编码后的 */ byte[] encode(byte[] src); String encodeToString(byte[] src); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/secury/Base64Impl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.secury; import java.util.Base64.Decoder; import java.util.Base64.Encoder; import org.yx.conf.AppInfo; public final class Base64Impl implements Base64 { public static final Base64 inst = new Base64Impl(java.util.Base64.getEncoder(), java.util.Base64.getMimeDecoder()); private final Decoder decoder; private final Encoder encoder; public Base64Impl(Encoder encoder, Decoder decoder) { this.encoder = encoder; this.decoder = decoder; } @Override public byte[] decode(byte[] src) { return decoder.decode(src); } @Override public byte[] decode(String src) { return decoder.decode(src.getBytes(AppInfo.UTF8)); } @Override public byte[] encode(byte[] src) { return encoder.encode(src); } @Override public String encodeToString(byte[] src) { return new String(encoder.encode(src), AppInfo.UTF8); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/secury/CommonDigest.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.secury; import java.nio.charset.Charset; import java.security.MessageDigest; public class CommonDigest implements Hasher { protected final String algorithm; public CommonDigest(String algorithm) { this.algorithm = algorithm; } public String digest(String data, Charset charset) throws Exception { return digestByteToString(data.getBytes(charset)); } public byte[] digest(byte[] data) throws Exception { MessageDigest md = MessageDigest.getInstance(algorithm); md.update(data); return md.digest(); } public String parseByte2HexStr(byte buf[]) { StringBuilder sb = new StringBuilder(32); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex); } return sb.toString().toLowerCase(); } @Override public String digestByteToString(byte[] data) throws Exception { return parseByte2HexStr(digest(data)); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/secury/Encryptor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.secury; public interface Encryptor { public byte[] encrypt(byte[] contentBytes, byte[] key) throws Exception; public byte[] decrypt(byte[] contentBytes, byte[] key) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/util/secury/Hasher.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.util.secury; import java.nio.charset.Charset; public interface Hasher { String digest(String data, Charset charset) throws Exception; byte[] digest(byte[] data) throws Exception; String digestByteToString(byte[] data) throws Exception; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/AbstractParamInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import java.util.Objects; import org.yx.annotation.spec.ParamSpec; import org.yx.conf.AppInfo; public abstract class AbstractParamInfo implements ParameterInfo { private final ParamSpec param; private final boolean required; private final boolean complex; private final boolean maybeCheck; public AbstractParamInfo(ParamSpec param, Class type) { this.param = Objects.requireNonNull(param); this.required = param.required() && AppInfo.getBoolean("sumk.param.required.enable", true); this.complex = Validators.supportComplex(Objects.requireNonNull(type)) && param.complex(); this.maybeCheck = this.required || this.complex || param.max() >= 0 || param.min() >= 0 || param.custom() != null; } @Override public Object custom() { return this.param.custom(); } @Override public String example() { return this.param.example(); } @Override public String comment() { return this.param.comment(); } public ParamSpec getParam() { return param; } public boolean isRequired() { return required; } public int getMax() { return param.max(); } public int getMin() { return param.min(); } @Override public String getCnName() { return this.param.value(); } public boolean isComplex() { return complex; } public boolean maybeCheck() { return maybeCheck; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/ComplexParamValidator.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import java.lang.reflect.Array; import java.util.List; import org.yx.annotation.Bean; import org.yx.exception.SimpleSumkException; import org.yx.log.Logs; @Bean public class ComplexParamValidator implements Validator { @Override public void valid(final ParameterInfo info, Object arg) throws InvalidParamException { if (info == null || arg == null) { return; } Class clz = arg.getClass(); if (clz.isArray()) { int length = Array.getLength(arg); if (length == 0) { return; } for (int i = 0; i < length; i++) { Validators.check(info, Array.get(arg, i)); } return; } if (!info.isComplex()) { return; } this.checkFields(FieldParameterHolder.get(clz), arg); } protected void checkFields(List infos, Object obj) throws InvalidParamException { if (infos == null) { return; } try { for (FieldParameterInfo info : infos) { Validators.check(info, info.field.get(obj)); } } catch (Exception e) { if (e instanceof InvalidParamException) { throw (InvalidParamException) e; } Logs.system().warn("参数校验发生异常," + e.getLocalizedMessage(), e); throw new SimpleSumkException(5346614, "校验时发生异常,异常信息为" + e.getLocalizedMessage()); } } @Override public int order() { return 10000; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/FieldParameterHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.yx.annotation.spec.ParamSpec; import org.yx.annotation.spec.Specs; import org.yx.util.CollectionUtil; public final class FieldParameterHolder { private static final ConcurrentMap, List> map = new ConcurrentHashMap<>(); public static void put(Class clz, List infos) { if (clz == null || infos == null || infos.isEmpty()) { return; } map.put(clz, infos); } public static Set> keys() { return new HashSet<>(map.keySet()); } public static List get(Class clz) { return map.get(clz); } public static Map getFieldParameterMap(Class clz) { List infos = get(clz); if (infos == null) { return Collections.emptyMap(); } Map infoMap = new HashMap<>(); for (FieldParameterInfo info : infos) { infoMap.put(info.getField(), info); } return infoMap; } public static void registerFieldInfo(final Class clazz) { if (clazz.isArray()) { registerFieldInfo(clazz.getComponentType()); return; } if (!Validators.supportComplex(clazz)) { return; } if (get(clazz) != null) { return; } List list = new ArrayList<>(); Class tempClz = clazz; while (tempClz != null && !tempClz.getName().startsWith("java.")) { Field[] fs = tempClz.getDeclaredFields(); for (Field f : fs) { if (Modifier.isStatic(f.getModifiers())) { continue; } ParamSpec p = Specs.extractParamField(f); if (p == null) { continue; } FieldParameterInfo info = new FieldParameterInfo(p, f); if (!info.maybeCheck()) { continue; } list.add(info); if (info.isComplex()) { registerFieldInfo(info.getParamType()); } } tempClz = tempClz.getSuperclass(); } if (list.size() > 0) { put(clazz, CollectionUtil.unmodifyList(list.toArray(new FieldParameterInfo[list.size()]))); } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/FieldParameterInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import java.lang.reflect.Field; import java.util.Objects; import org.yx.annotation.spec.ParamSpec; public class FieldParameterInfo extends AbstractParamInfo { protected final Field field; public FieldParameterInfo(ParamSpec param, Field field) { super(param, Objects.requireNonNull(field).getType()); this.field = field; if (!this.field.isAccessible()) { this.field.setAccessible(true); } } public String getParamName() { return field.getName(); } public Class getParamType() { return field.getType(); } public Field getField() { return field; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/InvalidParamException.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import org.yx.conf.AppInfo; import org.yx.util.StringUtil; /** * 这个异常表示是参数验证失败 * * @author 游夏 * */ public class InvalidParamException extends Exception { private static final long serialVersionUID = 125465546L; public static final String PARAM_SLOT = "$@$"; private ParameterInfo info; public InvalidParamException(String message, ParameterInfo info) { super(message); this.info = info; } @Override public String getMessage() { String ret = super.getMessage(); if (ret.indexOf(PARAM_SLOT) < 0) { return ret; } if (info == null) { return ret.replace(PARAM_SLOT, genericParamName()); } if (AppInfo.getBoolean("sumk.valid.name.cn", true) && StringUtil.isNotEmpty(info.getCnName())) { return ret.replace(PARAM_SLOT, info.getCnName()); } return ret.replace(PARAM_SLOT, info.getParamName()); } private String genericParamName() { return AppInfo.get("sumk.valid.name.generic", "参数"); } public ParameterInfo getInfo() { return info; } @Override public Throwable fillInStackTrace() { return this; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/ManuParameterInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; public class ManuParameterInfo implements ParameterInfo { private String paramName; private String cnName; private boolean required; private Integer max; private Integer min; private Class paramType; private boolean complex; private String custom; private String example; private String comment; public boolean isComplex() { return complex; } public void setComplex(boolean complex) { this.complex = complex; } public String getParamName() { return paramName; } public void setParamName(String paramName) { this.paramName = paramName; } public String getCnName() { return cnName; } public void setCnName(String cnName) { this.cnName = cnName; } public boolean isRequired() { return required; } public void setRequired(boolean required) { this.required = required; } public int getMax() { return max == null ? -1 : max; } public void setMax(Integer max) { this.max = max; } public int getMin() { return min == null ? -1 : min; } public void setMin(Integer min) { this.min = min; } public Class getParamType() { return paramType; } public void setParamType(Class paramType) { this.paramType = paramType; } public String custom() { return custom; } public void setCustom(String custom) { this.custom = custom; } public String example() { return example; } public void setExample(String example) { this.example = example; } public String comment() { return comment; } public void setComment(String comment) { this.comment = comment; } @Override public boolean maybeCheck() { return this.required || this.complex || max >= 0 || min >= 0 || custom != null; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/ParamInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import java.util.Objects; import org.yx.annotation.spec.ParamSpec; public class ParamInfo extends AbstractParamInfo { private final String paramName; private final Class paramType; public ParamInfo(ParamSpec param, String paramName, Class type) { super(param, type); this.paramName = Objects.requireNonNull(paramName); this.paramType = type; } public String getParamName() { return paramName; } public Class getParamType() { return paramType; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/ParameterInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; public interface ParameterInfo { String getParamName(); String getCnName(); boolean isRequired(); int getMax(); int getMin(); Class getParamType(); boolean isComplex(); Object custom(); String example(); String comment(); boolean maybeCheck(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/SimpleParamValidator.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import org.yx.annotation.Bean; import org.yx.common.locale.I18n; import org.yx.conf.AppInfo; @Bean public class SimpleParamValidator implements Validator { @Override public void valid(ParameterInfo info, Object arg) throws InvalidParamException { if (info.isRequired()) { if (arg == null || "".equals(arg)) { throw new InvalidParamException( AppInfo.get("sumk.valid.msg.null", InvalidParamException.PARAM_SLOT + "不能为空"), info); } } if (arg == null) { return; } String msg = buildMaxMessage(info.getMax(), arg); if (msg != null) { throw new InvalidParamException(msg, info); } msg = buildMinMessage(info.getMin(), arg); if (msg != null) { throw new InvalidParamException(msg, info); } } public static String buildMaxMessage(int expect, Object arg) { if (expect < 0) { return null; } Class clz = arg.getClass(); if (String.class == clz) { String s = (String) arg; if (s.length() > expect) { return I18n.get("sumk.valid.msg.maxLength", InvalidParamException.PARAM_SLOT + "的长度最大{0},实际却是{1}", expect, s.length()); } } if (Number.class.isAssignableFrom(clz)) { long n = ((Number) arg).longValue(); if (n > expect) { return I18n.get("sumk.valid.msg.max", InvalidParamException.PARAM_SLOT + "的值最大{0},实际却是{1}", expect, arg); } } return null; } public static String buildMinMessage(int expect, Object arg) { if (expect < 0) { return null; } Class clz = arg.getClass(); if (String.class == clz) { String s = (String) arg; if (s.length() < expect) { return I18n.get("sumk.valid.msg.minLength", InvalidParamException.PARAM_SLOT + "的长度最小{0},实际却是{1}", expect, s.length()); } } if (Number.class.isAssignableFrom(clz)) { long n = ((Number) arg).longValue(); if (n < expect) { return I18n.get("sumk.valid.msg.min", InvalidParamException.PARAM_SLOT + "的值最小{0},实际却是{1}", expect, arg); } } return null; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/Validator.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import org.yx.base.Ordered; public interface Validator extends Ordered { void valid(ParameterInfo info, Object arg) throws InvalidParamException; } ================================================ FILE: sumk-framework/src/main/java/org/yx/common/validate/Validators.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.common.validate; import java.util.Collection; import java.util.List; import java.util.Map; import org.yx.bean.IOC; import org.yx.util.SumkDate; public class Validators { private static Validator[] validators = new Validator[0]; public static void check(ParameterInfo info, Object arg) throws InvalidParamException { if (info == null || !info.maybeCheck()) { return; } Validator[] validators = Validators.validators; for (Validator v : validators) { v.valid(info, arg); } } public static synchronized void init() { if (validators.length > 0) { return; } List list = IOC.getBeans(Validator.class); if (list == null || list.isEmpty()) { return; } validators = list.toArray(new Validator[list.size()]); } public static boolean supportComplex(Class clazz) { if (clazz.isArray()) { return supportComplex(clazz.getComponentType()); } if (clazz.isPrimitive() || clazz.getName().startsWith("java.") || clazz == SumkDate.class || Map.class.isAssignableFrom(clazz) || Collection.class.isAssignableFrom(clazz)) { return false; } return true; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/main/StartConstants.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.main; public abstract class StartConstants { public static final String INNER_PACKAGE = String.join(".", "o" + "r" + "g", "y" + "x"); public static final String IOC_PACKAGES = "sumk.ioc"; public static final String NOSOA = "nosoa"; public static final String NOSOA_ClIENT = "nosoaClient"; public static final String NOHTTP = "nohttp"; public static final String EMBED_WEBSERVER_DISABLE = "sumk.webserver.disable"; public static final String THREAD_ON_DEAMON = "daemon"; } ================================================ FILE: sumk-framework/src/main/java/org/yx/main/SumkServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.main; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import org.yx.base.context.AppContext; import org.yx.bean.Booter; import org.yx.bean.IOC; import org.yx.bean.InnerIOC; import org.yx.bean.Plugin; import org.yx.bean.aop.asm.AsmUtils; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.conf.SystemConfig; import org.yx.conf.SystemConfigHolder; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.log.RawLog; import org.yx.redis.RedisPool; import org.yx.util.Loader; import org.yx.util.StringUtil; import org.yx.util.SumkThreadPool; public final class SumkServer { private static boolean httpEnable; private static boolean rpcEnable; private static long startTime; public static long startTime() { return startTime; } /** * 如果使用BootWatcher来加载远程配置,需要在BootWatcher里调用本方法 */ public static void reloadConfig() { AppContext sc = AppContext.inst(); sc.setTest(AppInfo.getBoolean("sumk.test", false)); SumkServer.rpcEnable = sc.get(StartConstants.NOSOA) == null && soaPort() >= 0; SumkServer.httpEnable = sc.get(StartConstants.NOHTTP) == null && httpPort() > 0; } public static boolean isHttpEnable() { return httpEnable; } public static boolean isRpcEnable() { return rpcEnable; } public static void main(String[] args) { run(null, args); } public static void run(Class mainClazz, String[] args) { start(mainClazz, args == null ? Collections.emptyList() : Arrays.asList(args)); } public static void startAsTool(Class mainClazz) { start(mainClazz, Arrays.asList(StartConstants.NOHTTP, StartConstants.NOSOA, StartConstants.NOSOA_ClIENT)); } public static synchronized void start(Class mainClazz, SystemConfig config, Collection args) { if (config != null) { SystemConfigHolder.setSystemConfig(config); } start(mainClazz, args); } /** * 启动ioc框架 * * @param mainClazz 类似于springboot的启动类,会扫描该类底下的类。支持为null * @param args 启动参数,可为null */ public static synchronized void start(Class mainClazz, Collection args) { if (AppContext.inst().isStarted()) { return; } startTime = System.currentTimeMillis(); AppContext.inst().setStatusToStarted(); if (mainClazz != null && mainClazz.getClassLoader() != null) { Loader.setClassLoader(mainClazz.getClassLoader()); } if (args == null) { args = Collections.emptyList(); } try { handleArgs(args); beforeStart(); AppContext sc = AppContext.inst(); if (Logs.system().isDebugEnabled()) { Logs.system().debug("start contexts:{}", sc); } if (sc.get(StartConstants.THREAD_ON_DEAMON) != null) { SumkThreadPool.setDaemon(true); } reloadConfig(); List pcks = getIocScanPath(mainClazz); new Booter().start(pcks); SumkThreadPool.scheduleThreadPoolMonitor(); RawLog.setLogger(RawLog.SLF4J_LOG); AppContext.clear(); } catch (Throwable e) { Log.printStack("sumk.error", e); try { Thread.sleep(1000); } catch (InterruptedException e1) { Thread.currentThread().interrupt(); } AppContext.startFailed(); } Logs.system().info("start finished in {} ms. sumk version {}", System.currentTimeMillis() - startTime, Const.sumkVersion()); } private static List getIocScanPath(Class mainClazz) { String ioc = AppInfo.getLatin(StartConstants.IOC_PACKAGES); List pcks = StringUtil.isEmpty(ioc) ? Collections.emptyList() : StringUtil.splitAndTrim(ioc, Const.COMMA, Const.SEMICOLON); pcks = new ArrayList<>(pcks); if (mainClazz != null && mainClazz.getPackage() != null && StringUtil.isNotEmpty(mainClazz.getPackage().getName())) { String pkName = mainClazz.getPackage().getName(); for (String defined : pcks) { if (defined.startsWith(pkName + ".")) { return pcks; } } pcks.add(pkName); } return pcks; } /** * 有时候希望在start之前做一些装配工作,这个方法就是用于完成这个目的。
* 通过sumk.start.before.class.xx=类名(必须实现Runnable接口)来配置 */ private static void beforeStart() { String prefix = "sumk.start.before.class."; Map map = AppInfo.subMap(prefix); if (map == null || map.isEmpty()) { return; } map = new TreeMap<>(map); for (String key : map.keySet()) { String clzName = map.get(key); if (StringUtil.isEmpty(clzName)) { continue; } try { clzName = StringUtil.toLatin(clzName).trim(); Object obj = Loader.newInstance(clzName); if (!(obj instanceof Runnable)) { Logs.ioc().warn("{}的处理类{}不是Runnable类型,被自动过滤掉", prefix + key, clzName); continue; } ((Runnable) obj).run(); } catch (Exception e) { Logs.ioc().error(e.getLocalizedMessage(), e); AppContext.startFailed(); } } } private static void handleArgs(Collection args) { if (args == null) { return; } for (String arg : args) { if (arg.contains("=")) { String[] kv = arg.split("=", 2); AppContext.inst().put(kv[0], kv[1]); continue; } AppContext.inst().put(arg, Boolean.TRUE); } } public static boolean stop() { synchronized (SumkServer.class) { if (AppContext.inst().isDestoryed()) { return false; } AppContext.inst().setStatusToDestoryed(); } Logs.system().warn("sumk server stoping..."); List lifes = IOC.getBeans(Plugin.class); if (lifes != null && lifes.size() > 0) { Collections.reverse(lifes); for (Plugin b : lifes) { try { b.stop(); } catch (Exception e) { Log.printStack("sumk.error", e); } } } try { RedisPool.shutdown(); } catch (Throwable e2) { } AsmUtils.clearProxyClassLoaders(); InnerIOC.clear(); Logs.system().info("sumk server stoped!!!"); return true; } public static void destroy() { if (!stop()) { return; } SumkThreadPool.shutdown(); } public static Executor getExecutor(String name) { Object obj = AppContext.inst().get(name); if (obj == null || !ExecutorService.class.isInstance(obj)) { return SumkThreadPool.executor(); } return (Executor) obj; } public static ThreadPoolExecutor getHttpExecutor() { Object obj = AppContext.inst().get("sumk.http.executor"); if (obj == null || !ThreadPoolExecutor.class.isInstance(obj)) { return (ThreadPoolExecutor) SumkThreadPool.executor(); } return (ThreadPoolExecutor) obj; } /** * * @return 剩余的启动超时时间,以ms为单位 */ public static long startTimeout() { return SumkServer.startTime() + AppInfo.getLong("sumk.start.timeout", 1000L * 60 * 10) - System.currentTimeMillis(); } public static String soaHost() { return AppInfo.get("sumk.rpc.host", AppInfo.getLocalIp()); } public static int soaPort() { return AppInfo.getInt("sumk.rpc.port", -1); } public static String httpHost() { return AppInfo.get("sumk.http.host", null); } public static int httpPort() { return AppInfo.getInt("sumk.http.port", -1); } /** * 重置状态,一般用于单元测试 */ public static void resetStatus() { AppContext.inst().resetStatus(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/Checkable.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; public interface Checkable { boolean aliveCheck(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/Redis.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import org.yx.redis.command.BinaryJedisCommand; import org.yx.redis.command.JedisCommand; import org.yx.redis.command.MultiKeyCommand; import org.yx.redis.command.ScriptingCommand; public interface Redis extends BinaryJedisCommand, JedisCommand, MultiKeyCommand, ScriptingCommand { /** * @return redis的主机地址,如果存在多个,就用逗号分隔 */ String hosts(); /** * 返回的config只是用来查看,不要去修改它。对它的修改没有意义 * * @return 构造redis对象所使用的配置 */ RedisConfig getRedisConfig(); void shutdownPool(); RedisType redisType(); /** * 如果发生了异常,返回null代替抛出异常。 一般而言它只对普通redis起作用,对于redis集群,该方法不一定有作用。 * 通过isMuted()可以判断是否生效 * * @return Redis对象,不为null */ Redis mute(); boolean isMuted(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisChecker.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import org.yx.common.util.helper.ArrayHelper; import org.yx.log.Logs; public final class RedisChecker implements Runnable { private static final RedisChecker holder = new RedisChecker(); private RedisChecker() { } public static RedisChecker get() { return holder; } private Checkable[] allRedis = new Checkable[0]; public synchronized void addRedis(Checkable c) { Logs.redis().debug("add alive check: {}", c); this.allRedis = ArrayHelper.add(this.allRedis, c, Checkable.class); } @Override public void run() { Checkable[] rediss = this.allRedis; for (Checkable redis : rediss) { redis.aliveCheck(); } } public void remove(Checkable c) { Logs.redis().debug("remove alive chech: {}", c); this.allRedis = ArrayHelper.remove(this.allRedis, c, Checkable.class); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.yx.exception.SumkException; import org.yx.util.StringUtil; @SuppressWarnings("rawtypes") public class RedisConfig extends GenericObjectPoolConfig { private final String hosts; private int db = 0; private int connectionTimeout = 5000; private int timeout = 2000; private String user; public String getUser() { return user; } public void setUser(String user) { this.user = user; } private String password; private int maxAttempts = 3; private String master; private String alias; /** * 1 cluster 2 sentinel 其它是普通 */ private String type; @SuppressWarnings("deprecation") public RedisConfig(String host) { if (host == null || host.isEmpty()) { throw new SumkException(657645465, "redis host cannot be empty"); } this.hosts = StringUtil.toLatin(host.trim()); setTestWhileIdle(true); setMinEvictableIdleTimeMillis(60000); setTimeBetweenEvictionRunsMillis(30000); } public String getMaster() { return master; } public void setMaster(String master) { this.master = master; } public String hosts() { return hosts; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getDb() { return db; } public void setDb(int db) { this.db = db; } public int getMaxAttempts() { return maxAttempts; } public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } @Override public String toString() { return "hosts=" + hosts + ", db=" + db + ", user=" + user + ", password=" + password + ", timeout=" + timeout + ", maxAttempts=" + maxAttempts + " , " + super.toString(); } public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisCounter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import org.yx.common.sequence.SeqCounter; public final class RedisCounter implements SeqCounter { private final Redis redis; @Override public int incr(String name) { if (name == null || name.isEmpty()) { return redis.incr("__SEQ_GLOBAL_FOR_ALL").intValue(); } Redis r = RedisPool.getRedisExactly(name); if (r == null) { r = this.redis; } return r.incr(name).intValue(); } public RedisCounter(Redis redis) { this.redis = redis; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import org.yx.base.Ordered; public interface RedisFactory extends Ordered { Redis create(RedisConfig config); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisLoader.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.yx.bean.IOC; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.conf.SimpleBeanUtil; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class RedisLoader { public static final String SEQ = "seq"; public static final String SESSION = "session"; private static final String CONFIG_PREFIX = "s.redis."; public static synchronized void init() throws Exception { Map map = AppInfo.subMap(CONFIG_PREFIX); if (map == null || map.isEmpty()) { return; } Map commonConfig = Collections.unmodifiableMap(AppInfo.subMap("s.common.redis.")); for (String key : map.keySet()) { if (key.contains(".")) { continue; } Map configMap = new HashMap<>(commonConfig); configMap.putAll(CollectionUtil.subMap(map, key + ".")); initRedis(key, StringUtil.toLatin(map.get(key).trim()), configMap); } if (Logs.redis().isDebugEnabled()) { Logs.redis().debug("redis的主键列表{},默认redis: {}", RedisPool.names(), RedisPool.defaultRedis()); } } public static void initRedis(final String name, String host, Map configMap) { Logs.redis().info("开始初始化redis:{}={}", name, host); try { RedisConfig config = createConfig(host, configMap); Logs.redis().debug("{} : {}", name, config); Redis redis = IOC.getFirstBean(RedisFactory.class, false).create(config); if ("*".equals(name) || "default".equals(name)) { RedisPool.setDefaultRedis(redis); } else { RedisPool.putIfAbsent(name, redis); } String aliases = config.getAlias(); if (StringUtil.isNotEmpty(aliases)) { aliases = StringUtil.toLatin(aliases); for (String s : StringUtil.splitAndTrim(aliases, ",")) { if (RedisPool.getRedisExactly(s) != null) { Logs.redis().warn("{}的redis配置已经存在了", s); continue; } Logs.redis().debug("设置别名{} -> {}", s, name); RedisPool.putIfAbsent(s, redis); } } } catch (Exception e) { throw new SumkException(35345, "redis [" + name + "] 初始化失败", e); } } public static RedisConfig createConfig(String host, Map configMap) { RedisConfig config = new RedisConfig(host); if (configMap != null && configMap.size() > 0) { try { SimpleBeanUtil.copyProperties(config, configMap); if (AppInfo.getBoolean("sumk.redis.password.encry", false) && StringUtil.isNotEmpty(config.getPassword())) { byte[] bs = S.base64().decode(config.getPassword().getBytes()); String p2 = new String(S.cipher().decrypt(bs, RedisSettings.getPasswordKey())); config.setPassword(p2); } } catch (Exception e) { Logs.redis().error(e.getLocalizedMessage(), e); } } return config; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisPlugin.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import java.util.concurrent.TimeUnit; import org.yx.annotation.Bean; import org.yx.bean.Plugin; import org.yx.common.lock.Locker; import org.yx.common.sequence.SeqHolder; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.Loader; import org.yx.util.Task; @Bean public class RedisPlugin implements Plugin { @Override public int order() { return 50; } @Override public void prepare() { if (AppInfo.subMap("s.redis.").isEmpty()) { return; } try { Loader.loadClass("redis.clients.jedis.Jedis"); } catch (Throwable e) { Logs.redis().warn("Jedis is not in use because of " + e.getMessage()); return; } try { Logs.redis().debug("redis pool init"); RedisLoader.init(); initSeqUtilCounter(); Task.scheduleAtFixedRate(RedisChecker.get(), 5, AppInfo.getInt("sumk.redis.check.period", 5), TimeUnit.SECONDS); } catch (Exception e) { Logs.redis().error(e.getMessage(), e); throw new SumkException(35345436, "redis启动失败"); } Locker.init(); } @Override public void startAsync() { } private static void initSeqUtilCounter() { if (SeqHolder.inst().getCounter() != null) { return; } Redis redis = RedisPool.getRedisExactly(AppInfo.get("sumk.counter.name", RedisLoader.SEQ)); if (redis == null) { redis = RedisPool.getRedisExactly("session"); } if (redis != null) { Logs.redis().debug("use redis counter"); SeqHolder.inst().setCounter(new RedisCounter(redis)); } } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisPool.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.yx.log.Logs; public final class RedisPool { private static Map cache = Collections.emptyMap(); private static Redis _defaultRedis; static void setDefaultRedis(Redis r) { _defaultRedis = r; } public static List names() { return new ArrayList(cache.keySet()); } public static Redis get(String alias) { if (alias == null) { return _defaultRedis; } return cache.getOrDefault(alias, _defaultRedis); } public static Redis getRedisExactly(String alias) { return cache.get(alias); } public static Redis defaultRedis() { return _defaultRedis; } public static synchronized Redis put(String alias, Redis redis) { Map map = new HashMap<>(cache); Redis old = map.put(alias, Objects.requireNonNull(redis)); cache = map; Logs.redis().info("redis name replace to {} : {}", alias, redis); return old; } public static synchronized boolean putIfAbsent(String alias, Redis redis) { Map map = new HashMap<>(cache); Redis old = map.putIfAbsent(alias, Objects.requireNonNull(redis)); if (old == null) { cache = map; return true; } return false; } public static synchronized Redis remove(String alias) { Map map = new HashMap<>(cache); Redis old = map.remove(alias); if (old != null) { cache = map; } return old; } public static void shutdown() { Set redises = new HashSet<>(cache.values()); if (_defaultRedis != null) { redises.add(_defaultRedis); } for (Redis r : redises) { r.shutdownPool(); } cache = Collections.emptyMap(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisSettings.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.yx.common.Host; import org.yx.util.StringUtil; public final class RedisSettings { private static byte[] PASSWORD_KEY = new byte[] { 121, 111, 117, 116, 111, 110, 103, 108, 117, 97, 110, 64, 115, 117, 109, 107 }; public static byte[] getPasswordKey() { return PASSWORD_KEY; } public static void setPasswordKey(byte[] passwordKey) { PASSWORD_KEY = Objects.requireNonNull(passwordKey); } public static List parseHosts(String host) { String h = StringUtil.toLatin(host).replaceAll("\\s", ""); String[] hs = h.split(","); List hosts = new ArrayList<>(hs.length); for (String addr : hs) { if (addr.isEmpty()) { continue; } if (!addr.contains(":")) { hosts.add(Host.create(addr, 6379)); continue; } hosts.add(Host.create(addr)); } return hosts; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/RedisType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis; public enum RedisType { SIMPLE, CLUSTER, /** * 实验性功能 */ SENTINEL, OTHER; public boolean accept(String type) { if (type == null || type.isEmpty()) { return false; } if (this.name().equalsIgnoreCase(type)) { return true; } if (String.valueOf(this.ordinal()).equals(type)) { return true; } return false; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/command/BinaryJedisCommand.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.command; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; public interface BinaryJedisCommand { String set(byte[] key, byte[] value); String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time); byte[] get(byte[] key); Boolean exists(byte[] key); String type(byte[] key); Long pexpire(byte[] key, long milliseconds); Long pttl(byte[] key); Long setrange(byte[] key, long offset, byte[] value); byte[] getrange(byte[] key, long startOffset, long endOffset); Long hset(byte[] key, byte[] field, byte[] value); Long hset(byte[] key, Map hash); byte[] hget(byte[] key, byte[] field); Long hsetnx(byte[] key, byte[] field, byte[] value); String hmset(byte[] key, Map hash); List hmget(byte[] key, byte[]... fields); Boolean hexists(byte[] key, byte[] field); Long hdel(byte[] key, byte[]... field); Set hkeys(byte[] key); Collection hvals(byte[] key); Map hgetAll(byte[] key); Long rpush(byte[] key, byte[]... args); Long lpush(byte[] key, byte[]... args); Long llen(byte[] key); List lrange(byte[] key, long start, long stop); String ltrim(byte[] key, long start, long stop); byte[] lindex(byte[] key, long index); String lset(byte[] key, long index, byte[] value); Long lrem(byte[] key, long count, byte[] value); byte[] lpop(byte[] key); byte[] rpop(byte[] key); Long del(byte[] key); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/command/JedisCommand.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.command; import java.util.List; import java.util.Map; import java.util.Set; public interface JedisCommand { String set(String key, String value); String set(String key, String value, String nxxx, String expx, long time); String set(String key, String value, String expx, long time); String set(String key, String value, String nxxx); String get(String key); Boolean exists(String key); Long persist(String key); String type(String key); byte[] dump(String key); String restore(String key, int ttl, byte[] serializedValue); Long expire(String key, int seconds); Long pexpire(String key, long milliseconds); Long expireAt(String key, long unixTime); Long pexpireAt(String key, long millisecondsTimestamp); Long ttl(String key); Long pttl(String key); Long touch(String key); Boolean setbit(String key, long offset, boolean value); Boolean getbit(String key, long offset); Long setrange(String key, long offset, String value); String getrange(String key, long startOffset, long endOffset); String getSet(String key, String value); Long setnx(String key, String value); String setex(String key, int seconds, String value); String psetex(String key, long milliseconds, String value); Long decrBy(String key, long decrement); Long decr(String key); Long incrBy(String key, long increment); Double incrByFloat(String key, double increment); Long incr(String key); Long append(String key, String value); String substr(String key, int start, int end); Long hset(String key, String field, String value); Long hset(String key, Map hash); String hget(String key, String field); Long hsetnx(String key, String field, String value); String hmset(String key, Map hash); List hmget(String key, String... fields); Long hincrBy(String key, String field, long value); Double hincrByFloat(String key, String field, double value); Boolean hexists(String key, String field); Long hdel(String key, String... field); Long hlen(String key); Set hkeys(String key); List hvals(String key); Map hgetAll(String key); Long rpush(String key, String... string); Long lpush(String key, String... string); Long llen(String key); List lrange(String key, long start, long stop); String ltrim(String key, long start, long stop); String lindex(String key, long index); String lset(String key, long index, String value); Long lrem(String key, long count, String value); String lpop(String key); String rpop(String key); Long sadd(String key, String... member); Set smembers(String key); Long srem(String key, String... member); String spop(String key); Set spop(String key, long count); Long scard(String key); Boolean sismember(String key, String member); String srandmember(String key); List srandmember(String key, int count); Long strlen(String key); Long zadd(String key, double score, String member); Long zadd(String key, Map scoreMembers); Set zrange(String key, long start, long stop); Long zrem(String key, String... members); Double zincrby(String key, double increment, String member); Long zrank(String key, String member); Long zrevrank(String key, String member); Set zrevrange(String key, long start, long stop); Long zcard(String key); Double zscore(String key, String member); List sort(String key); Long zcount(String key, double min, double max); Long zcount(String key, String min, String max); Set zrangeByScore(String key, double min, double max); Set zrangeByScore(String key, String min, String max); Set zrevrangeByScore(String key, double max, double min); Set zrangeByScore(String key, double min, double max, int offset, int count); Set zrevrangeByScore(String key, String max, String min); Set zrangeByScore(String key, String min, String max, int offset, int count); Set zrevrangeByScore(String key, double max, double min, int offset, int count); Set zrevrangeByScore(String key, String max, String min, int offset, int count); Long zremrangeByRank(String key, long start, long stop); Long zremrangeByScore(String key, double min, double max); Long zremrangeByScore(String key, String min, String max); Long zlexcount(String key, String min, String max); Set zrangeByLex(String key, String min, String max); Set zrangeByLex(String key, String min, String max, int offset, int count); Set zrevrangeByLex(String key, String max, String min); Set zrevrangeByLex(String key, String max, String min, int offset, int count); Long zremrangeByLex(String key, String min, String max); Long lpushx(String key, String... string); Long rpushx(String key, String... string); List blpop(int timeout, String key); List brpop(int timeout, String key); Long del(String key); Long unlink(String key); String echo(String string); Long move(String key, int dbIndex); Long bitcount(String key); Long bitcount(String key, long start, long end); Long pfadd(String key, String... elements); long pfcount(String key); Long geoadd(String key, double longitude, double latitude, String member); Double geodist(String key, String member1, String member2); List geohash(String key, String... members); List bitfield(String key, String... arguments); Long hstrlen(String key, String field); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/command/MultiKeyCommand.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.command; import java.util.List; import java.util.Set; public interface MultiKeyCommand { Long del(String... keys); Long unlink(String... keys); Long exists(String... keys); List blpop(int timeout, String... keys); List brpop(int timeout, String... keys); List blpop(String... args); List brpop(String... args); Set keys(String pattern); List mget(String... keys); String mset(String... keysvalues); Long msetnx(String... keysvalues); String rename(String oldkey, String newkey); Long renamenx(String oldkey, String newkey); String rpoplpush(String srckey, String dstkey); Set sdiff(String... keys); Long sdiffstore(String dstkey, String... keys); Set sinter(String... keys); Long sinterstore(String dstkey, String... keys); Long smove(String srckey, String dstkey, String member); Long sort(String key, String dstkey); Set sunion(String... keys); Long sunionstore(String dstkey, String... keys); String watch(String... keys); String unwatch(); Long zinterstore(String dstkey, String... sets); Long zunionstore(String dstkey, String... sets); String brpoplpush(String source, String destination, int timeout); Long publish(String channel, String message); String randomKey(); String pfmerge(String destkey, String... sourcekeys); long pfcount(String... keys); Long touch(String... keys); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/command/ScriptingCommand.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.command; import java.util.List; public interface ScriptingCommand { Object eval(String script, int keyCount, String... params); Object eval(String script, List keys, List args); Object eval(String script, String sampleKey); Object evalsha(String sha1, String sampleKey); Object evalsha(String sha1, List keys, List args); Object evalsha(String sha1, int keyCount, String... params); String scriptLoad(String script); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/AbstractJedisExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.util.Objects; import java.util.function.Function; import org.yx.exception.SimpleSumkException; import org.yx.exception.SumkException; import org.yx.exception.SumkExceptionCode; import org.yx.log.Logs; import org.yx.redis.RedisConfig; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.util.Pool; public abstract class AbstractJedisExecutor implements JedisExecutor { private static final SumkException DIS_CONNECTION_EXCEPTION = new SimpleSumkException( SumkExceptionCode.REDIS_DIS_CONNECTION, "redis is disConnected"); protected boolean disConnected; protected final RedisConfig config; protected final Pool pool; public AbstractJedisExecutor(RedisConfig config, Pool pool) { this.config = Objects.requireNonNull(config); this.pool = Objects.requireNonNull(pool); } public Jedis jedis() { return pool.getResource(); } @Override public RedisConfig getRedisConfig() { return this.config; } @Override public String hosts() { return config.hosts(); } protected boolean isConnectException(Throwable e) { return e instanceof JedisConnectionException || (e.getCause() != null && e.getCause() instanceof JedisConnectionException); } @Override public T execAndRetry(Function callback, boolean mute) { return this.exec(callback, this.config.getMaxAttempts(), mute); } public T exec(Function callback, int maxAttempts, boolean mute) { if (maxAttempts < 1) { maxAttempts = 1; } Jedis jedis = null; Throwable e1 = null; for (int i = 0; i < maxAttempts; i++) { if (this.disConnected) { if (mute) { return null; } throw DIS_CONNECTION_EXCEPTION; } try { jedis = this.jedis(); return callback.apply(jedis); } catch (Throwable e) { e1 = e; if (isConnectException(e)) { Logs.redis().warn("redis连接异常!({}){}", hosts(), e.getMessage()); continue; } Logs.redis().error("redis执行错误!({}){}", hosts(), e.getMessage()); break; } finally { if (jedis != null) { try { jedis.close(); } catch (Throwable e2) { Logs.redis().error(e2.toString(), e2); } jedis = null; } } } if (mute) { return null; } if (e1 != null) { throw new SumkException(12342422, e1.getMessage(), e1); } throw new SumkException(12342423, "未知redis异常,执行次数:" + maxAttempts); } public void shutdownPool() { this.pool.close(); } @Override public String toString() { StringBuilder sb = new StringBuilder(32); sb.append("[hosts=").append(hosts()).append(",db=").append(config.getDb()).append(",maxAttempts=") .append(config.getMaxAttempts()); try { sb.append(",idle=").append(pool.getNumIdle()).append(",active=").append(pool.getNumActive()); } catch (Throwable e) { } return sb.append("]").toString(); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/AbstractRedis.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.util.function.Function; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.redis.Checkable; import org.yx.redis.Redis; import org.yx.redis.RedisChecker; import org.yx.redis.RedisConfig; import org.yx.redis.RedisType; import redis.clients.jedis.Jedis; public abstract class AbstractRedis implements Redis, Redis3x, Cloneable { protected final JedisExecutor jedis2Executor; private boolean mute; private transient AbstractRedis muteConnectionExceptionRedis; public AbstractRedis(JedisExecutor executor) { this.jedis2Executor = executor; if (this.jedis2Executor instanceof Checkable) { RedisChecker.get().addRedis((Checkable) this.jedis2Executor); } } @Override public RedisConfig getRedisConfig() { return this.jedis2Executor.getRedisConfig(); } @Override public String hosts() { return jedis2Executor.hosts(); } @Override public T execute(String key, Function callback) { return this.execAndRetry(callback); } public T execAndRetry(Function callback) { return this.jedis2Executor.execAndRetry(callback, mute); } public void shutdownPool() { this.jedis2Executor.shutdownPool(); if (this.jedis2Executor instanceof Checkable) { RedisChecker.get().remove((Checkable) this.jedis2Executor); } } @Override public Redis mute() { AbstractRedis r = this.muteConnectionExceptionRedis; if (r != null) { return r; } if (this.mute) { return this; } try { r = (AbstractRedis) super.clone(); } catch (Exception e) { Logs.redis().error(e.toString(), e); throw new SumkException(345345, this.getClass().getName() + "无法clone"); } r.mute = true; this.muteConnectionExceptionRedis = r; return r; } @Override public boolean isMuted() { return mute; } @Override public RedisType redisType() { return this.jedis2Executor.redisType(); } @Override public String toString() { return "Redis[" + this.jedis2Executor + "]"; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/JedisExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.util.function.Function; import org.yx.redis.RedisConfig; import org.yx.redis.RedisType; import redis.clients.jedis.Jedis; public interface JedisExecutor { T execAndRetry(Function callback, boolean mute); void shutdownPool(); RedisConfig getRedisConfig(); String hosts(); RedisType redisType(); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/Redis3Factory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import org.yx.annotation.Bean; import org.yx.base.Ordered; import org.yx.log.Logs; import org.yx.redis.Redis; import org.yx.redis.RedisConfig; import org.yx.redis.RedisFactory; import org.yx.redis.RedisType; @Bean public class Redis3Factory implements RedisFactory { @Override public Redis create(RedisConfig conf) { String type = conf.getType(); if (RedisType.SENTINEL.accept(type)) { Logs.redis().warn("sentinel has not be tested.if any problem,send email to 3205207767@qq.com"); return new RedisImpl(new SentinelJedisExecutor(conf, Redis3Kit.createSentinelPool(conf))); } if (RedisType.CLUSTER.accept(type) || conf.hosts().contains(",")) { return Redis3Kit.createJedisCluster(conf); } return new RedisImpl(new SingleJedisExecutor(conf, Redis3Kit.createJedisPool(conf))); } @Override public int order() { return Ordered.DEFAULT_ORDER + 100; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/Redis3Kit.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.util.HashSet; import java.util.List; import java.util.Set; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.redis.RedisConfig; import org.yx.redis.RedisSettings; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisSentinelPool; import redis.clients.jedis.params.SetParams; public class Redis3Kit { public static SetParams createSetParams(String nxxx, String expx, long time) { SetParams params = SetParams.setParams(); if (nxxx != null) { if ("NX".equalsIgnoreCase(nxxx)) { params.nx(); } else if ("XX".equalsIgnoreCase(nxxx)) { params.xx(); } else { throw new SumkException(234325, "nxxx参数错误:" + nxxx); } } if (expx != null) { if ("EX".equalsIgnoreCase(expx)) { params.ex(time); } else if ("PX".equalsIgnoreCase(expx)) { params.px(time); } else { throw new SumkException(234325, "expx参数错误:" + expx); } } return params; } @SuppressWarnings("unchecked") public static JedisPool createJedisPool(RedisConfig conf) { List hosts = RedisSettings.parseHosts(conf.hosts()); Host h = hosts.get(0); try { return new JedisPool(conf, h.ip(), h.port(), conf.getConnectionTimeout(), conf.getTimeout(), conf.getUser(), conf.getPassword(), conf.getDb(), AppInfo.appId("sumk")); } catch (Throwable e) { Logs.redis().info("JedisPool ignore property user"); } try { return new JedisPool(conf, h.ip(), h.port(), conf.getConnectionTimeout(), conf.getTimeout(), conf.getPassword(), conf.getDb(), AppInfo.appId("sumk")); } catch (Throwable e) { Logs.redis().info("JedisPool ignore property connectionTimeout"); } return new JedisPool(conf, h.ip(), h.port(), conf.getTimeout(), conf.getPassword(), conf.getDb(), AppInfo.appId("sumk")); } @SuppressWarnings("unchecked") public static JedisSentinelPool createSentinelPool(RedisConfig config) { List hosts = RedisSettings.parseHosts(config.hosts()); Set sentinels = new HashSet<>(); for (Host h : hosts) { sentinels.add(h.toString()); } Logs.redis().info("create sentinel redis pool,sentinels={},db={}", sentinels, config.getDb()); try { return new JedisSentinelPool(config.getMaster(), sentinels, config, config.getConnectionTimeout(), config.getTimeout(), config.getUser(), config.getPassword(), config.getDb(), AppInfo.appId("sumk")); } catch (Throwable e) { Logs.redis().info("JedisCluster ignore property user"); } return new JedisSentinelPool(config.getMaster(), sentinels, config, config.getConnectionTimeout(), config.getTimeout(), config.getPassword(), config.getDb(), AppInfo.appId("sumk")); } public static RedisCluster createJedisCluster(RedisConfig config) { List hosts = RedisSettings.parseHosts(config.hosts()); Set hps = new HashSet<>(); for (Host h : hosts) { hps.add(new HostAndPort(h.ip(), h.port())); } Logs.redis().info("create JedisCluster redis pool,hosts={}", hps); try { return new RedisCluster(hps, config.getConnectionTimeout(), config.getTimeout(), config.getMaxAttempts(), config.getUser(), config.getPassword(), AppInfo.appId("sumk"), config); } catch (Throwable e) { Logs.redis().info("JedisCluster ignore property user"); } try { return new RedisCluster(hps, config.getConnectionTimeout(), config.getTimeout(), config.getMaxAttempts(), config.getPassword(), AppInfo.appId("sumk"), config); } catch (Throwable e) { Logs.redis().info("JedisCluster ignore property clientName"); } return new RedisCluster(hps, config.getConnectionTimeout(), config.getTimeout(), config.getMaxAttempts(), config.getPassword(), config); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/Redis3x.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.util.function.Function; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPubSub; /** * jedis2.x 额外增加的方法 */ public interface Redis3x { /** * 执行redis的批量操作或jedis的原生操作。
* 注意:如果是集群情况下,要保证所有的操作确实在key所对应的节点上才行,所以这个方法在集群环境里要慎重使用 * * @param 返回值类型 * @param sampleKey 只有在集群情况下才有意义,用于寻找真正的jedis节点 * @param callback 批处理的代码 * @return 返回的是callback的返回值 */ T execute(String sampleKey, Function callback); Long publish(String channel, String message); void subscribe(JedisPubSub jedisPubSub, String... channels); void psubscribe(final JedisPubSub jedisPubSub, final String... patterns); } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/RedisCluster.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.redis.Redis; import org.yx.redis.RedisConfig; import org.yx.redis.RedisType; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisClusterCommand; import redis.clients.jedis.JedisPool; import redis.clients.jedis.params.SetParams; public class RedisCluster extends JedisCluster implements Redis, Redis3x { protected final String hosts; protected final RedisConfig config; @SuppressWarnings("unchecked") public RedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String user, String password, String clientName, RedisConfig redisConfig) { super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, user, password, clientName, redisConfig); this.config = redisConfig; this.hosts = jedisClusterNode.toString(); } @SuppressWarnings("unchecked") public RedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, RedisConfig redisConfig) { super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, redisConfig); this.config = redisConfig; this.hosts = jedisClusterNode.toString(); } @SuppressWarnings("unchecked") public RedisCluster(Set jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String password, RedisConfig redisConfig) { super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, redisConfig); this.config = redisConfig; this.hosts = jedisClusterNode.toString(); } @Override public List blpop(String... args) { return super.blpop(AppInfo.getInt("sumk.redis.blpop.timeout", 1000), args); } @Override public List brpop(String... args) { return super.brpop(AppInfo.getInt("sumk.redis.brpop.timeout", 1000), args); } @Override public String watch(String... keys) { throw new UnsupportedOperationException(); } @Override public String unwatch() { throw new UnsupportedOperationException(); } @Override public String randomKey() { throw new UnsupportedOperationException(); } @Override public String hosts() { return this.hosts; } @Override public RedisConfig getRedisConfig() { return this.config; } @Override public void shutdownPool() { super.close(); } @Override public String toString() { return "RedisCluster " + hosts; } @Override public String scriptLoad(String script) { String ret = null; for (Map.Entry en : this.getClusterNodes().entrySet()) { try (Jedis jedis = en.getValue().getResource()) { ret = jedis.scriptLoad(script); } catch (RuntimeException e) { Logs.redis().error("分布式锁在{}初始化脚本失败,原因是:{}", en.getKey(), e); throw e; } } return ret; } @Override public T execute(String key, Function callback) { return new JedisClusterCommand(connectionHandler, maxAttempts) { @Override public T execute(Jedis connection) { return callback.apply(connection); } }.run(key); } @Override public Redis mute() { return this; } @Override public boolean isMuted() { return false; } @Override public RedisType redisType() { return RedisType.CLUSTER; } @Override public String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time) { String NXXX = new String(nxxx, StandardCharsets.UTF_8); String EXPX = new String(expx, StandardCharsets.UTF_8); SetParams params = Redis3Kit.createSetParams(NXXX, EXPX, time); return super.set(key, value, params); } @Override public String set(String key, String value, String nxxx, String expx, long time) { SetParams params = Redis3Kit.createSetParams(nxxx, expx, time); return super.set(key, value, params); } @Override public String set(String key, String value, String expx, long time) { SetParams params = Redis3Kit.createSetParams(null, expx, time); return super.set(key, value, params); } @Override public String set(String key, String value, String nxxx) { SetParams params = Redis3Kit.createSetParams(nxxx, null, -1); return super.set(key, value, params); } @Override public String restore(String key, int ttl, byte[] serializedValue) { return super.restore(key, (long) ttl, serializedValue); } @Override public Long expire(String key, int seconds) { return super.expire(key, (long) seconds); } @Override public String setex(String key, int seconds, String value) { return super.setex(key, (long) seconds, value); } @Override public Long move(String key, int dbIndex) { return -1L; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/RedisImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.JedisPubSub; public class RedisImpl extends AbstractRedis { public RedisImpl(JedisExecutor executor) { super(executor); } @Override public String get(String key) { return execAndRetry(jedis -> jedis.get(key)); } @Override public String type(String key) { return execAndRetry(jedis -> jedis.type(key)); } @Override public Long append(String key, String value) { return execAndRetry(jedis -> jedis.append(key, value)); } @Override public Set keys(String pattern) { return execAndRetry(jedis -> jedis.keys(pattern)); } @Override public String set(String key, String value, String nxxx) { return execAndRetry(jedis -> jedis.set(key, value, Redis3Kit.createSetParams(nxxx, null, -1))); } @Override public String set(String key, String value, String nxxx, String expx, long time) { return execAndRetry(jedis -> jedis.set(key, value, Redis3Kit.createSetParams(nxxx, expx, time))); } @Override public String set(String key, String value, String expx, long time) { return execAndRetry(jedis -> jedis.set(key, value, Redis3Kit.createSetParams(null, expx, time))); } @Override public String set(String key, String value) { return execAndRetry(jedis -> jedis.set(key, value)); } @Override public Boolean exists(String key) { return execAndRetry(jedis -> jedis.exists(key)); } @Override public Long exists(String... keys) { return execAndRetry(jedis -> jedis.exists(keys)); } @Override public String rename(String oldkey, String newkey) { return execAndRetry(jedis -> jedis.rename(oldkey, newkey)); } @Override public Long sort(String key, String dstkey) { return execAndRetry(jedis -> jedis.sort(key, dstkey)); } @Override public List sort(String key) { return execAndRetry(jedis -> jedis.sort(key)); } @Override public Long unlink(String... keys) { return execAndRetry(jedis -> jedis.unlink(keys)); } @Override public Long unlink(String key) { return execAndRetry(jedis -> jedis.unlink(key)); } @Override public List brpop(int timeout, String key) { return execAndRetry(jedis -> jedis.brpop(timeout, key)); } @Override public List brpop(int timeout, String... keys) { return execAndRetry(jedis -> jedis.brpop(timeout, keys)); } @Override public List brpop(String... args) { return execAndRetry(jedis -> jedis.brpop(args)); } @Override public byte[] dump(String key) { return execAndRetry(jedis -> jedis.dump(key)); } @Override public Object eval(String script, List keys, List args) { return execAndRetry(jedis -> jedis.eval(script, keys, args)); } @Override public Object eval(String script, int keyCount, String... params) { return execAndRetry(jedis -> jedis.eval(script, keyCount, params)); } @Override public Object eval(String script, String sampleKey) { return execAndRetry(jedis -> jedis.eval(script)); } @Override public Long sadd(String key, String... members) { return execAndRetry(jedis -> jedis.sadd(key, members)); } @Override public Double hincrByFloat(String key, String field, double value) { return execAndRetry(jedis -> jedis.hincrByFloat(key, field, value)); } @Override public List hvals(String key) { return execAndRetry(jedis -> jedis.hvals(key)); } @Override public List hmget(String key, String... fields) { return execAndRetry(jedis -> jedis.hmget(key, fields)); } @Override public Map hgetAll(String key) { return execAndRetry(jedis -> jedis.hgetAll(key)); } @Override public Long rpush(String key, String... strings) { return execAndRetry(jedis -> jedis.rpush(key, strings)); } @Override public Long llen(String key) { return execAndRetry(jedis -> jedis.llen(key)); } @Override public Long hlen(String key) { return execAndRetry(jedis -> jedis.hlen(key)); } @Override public String lindex(String key, long index) { return execAndRetry(jedis -> jedis.lindex(key, index)); } @Override public Long lpush(String key, String... strings) { return execAndRetry(jedis -> jedis.lpush(key, strings)); } @Override public Long lrem(String key, long count, String value) { return execAndRetry(jedis -> jedis.lrem(key, count, value)); } @Override public String lpop(String key) { return execAndRetry(jedis -> jedis.lpop(key)); } @Override public String lset(String key, long index, String value) { return execAndRetry(jedis -> jedis.lset(key, index, value)); } @Override public String rpop(String key) { return execAndRetry(jedis -> jedis.rpop(key)); } @Override public Long del(String... keys) { return execAndRetry(jedis -> jedis.del(keys)); } @Override public Long del(String key) { return execAndRetry(jedis -> jedis.del(key)); } @Override public String restore(String key, int ttl, byte[] serializedValue) { return execAndRetry(jedis -> jedis.restore(key, (long) ttl, serializedValue)); } @Override public Long hdel(String key, String... fields) { return execAndRetry(jedis -> jedis.hdel(key, fields)); } @Override public String getSet(String key, String value) { return execAndRetry(jedis -> jedis.getSet(key, value)); } @Override public String ltrim(String key, long start, long stop) { return execAndRetry(jedis -> jedis.ltrim(key, start, stop)); } @Override public String setex(String key, int seconds, String value) { return execAndRetry(jedis -> jedis.setex(key, (long) seconds, value)); } @Override public List lrange(String key, long start, long stop) { return execAndRetry(jedis -> jedis.lrange(key, start, stop)); } @Override public Long hsetnx(String key, String field, String value) { return execAndRetry(jedis -> jedis.hsetnx(key, field, value)); } @Override public String psetex(String key, long milliseconds, String value) { return execAndRetry(jedis -> jedis.psetex(key, milliseconds, value)); } @Override public Long persist(String key) { return execAndRetry(jedis -> jedis.persist(key)); } @Override public Set hkeys(String key) { return execAndRetry(jedis -> jedis.hkeys(key)); } @Override public Long setnx(String key, String value) { return execAndRetry(jedis -> jedis.setnx(key, value)); } @Override public Long decrBy(String key, long decrement) { return execAndRetry(jedis -> jedis.decrBy(key, decrement)); } @Override public Long decr(String key) { return execAndRetry(jedis -> jedis.decr(key)); } @Override public Long hset(String key, String field, String value) { return execAndRetry(jedis -> jedis.hset(key, field, value)); } @Override public Long hset(String key, Map hash) { return execAndRetry(jedis -> jedis.hset(key, hash)); } @Override public String hget(String key, String field) { return execAndRetry(jedis -> jedis.hget(key, field)); } @Override public String hmset(String key, Map hash) { return execAndRetry(jedis -> jedis.hmset(key, hash)); } @Override public Boolean hexists(String key, String field) { return execAndRetry(jedis -> jedis.hexists(key, field)); } @Override public Long incrBy(String key, long increment) { return execAndRetry(jedis -> jedis.incrBy(key, increment)); } @Override public Double incrByFloat(String key, double increment) { return execAndRetry(jedis -> jedis.incrByFloat(key, increment)); } @Override public Long incr(String key) { return execAndRetry(jedis -> jedis.incr(key)); } @Override public Long expire(String key, int seconds) { return execAndRetry(jedis -> jedis.expire(key, (long) seconds)); } @Override public Long pexpire(String key, long milliseconds) { return execAndRetry(jedis -> jedis.pexpire(key, milliseconds)); } @Override public String substr(String key, int start, int end) { return execAndRetry(jedis -> jedis.substr(key, start, end)); } @Override public Long hincrBy(String key, String field, long value) { return execAndRetry(jedis -> jedis.hincrBy(key, field, value)); } @Override public Long ttl(String key) { return execAndRetry(jedis -> jedis.ttl(key)); } @Override public Long pttl(String key) { return execAndRetry(jedis -> jedis.pttl(key)); } @Override public Long touch(String key) { return execAndRetry(jedis -> jedis.touch(key)); } @Override public Long touch(String... keys) { return execAndRetry(jedis -> jedis.touch(keys)); } @Override public Boolean setbit(String key, long offset, boolean value) { return execAndRetry(jedis -> jedis.setbit(key, offset, value)); } @Override public Long expireAt(String key, long unixTime) { return execAndRetry(jedis -> jedis.expireAt(key, unixTime)); } @Override public Long pexpireAt(String key, long millisecondsTimestamp) { return execAndRetry(jedis -> jedis.pexpireAt(key, millisecondsTimestamp)); } @Override public Boolean getbit(String key, long offset) { return execAndRetry(jedis -> jedis.getbit(key, offset)); } @Override public Long setrange(String key, long offset, String value) { return execAndRetry(jedis -> jedis.setrange(key, offset, value)); } @Override public String getrange(String key, long startOffset, long endOffset) { return execAndRetry(jedis -> jedis.getrange(key, startOffset, endOffset)); } @Override public Long msetnx(String... keysvalues) { return execAndRetry(jedis -> jedis.msetnx(keysvalues)); } @Override public Long zrevrank(String key, String member) { return execAndRetry(jedis -> jedis.zrevrank(key, member)); } @Override public Double zincrby(String key, double increment, String member) { return execAndRetry(jedis -> jedis.zincrby(key, increment, member)); } @Override public Long zrank(String key, String member) { return execAndRetry(jedis -> jedis.zrank(key, member)); } @Override public Long rpushx(String key, String... string) { return execAndRetry(jedis -> jedis.rpushx(key, string)); } @Override public List srandmember(String key, int count) { return execAndRetry(jedis -> jedis.srandmember(key, count)); } @Override public String srandmember(String key) { return execAndRetry(jedis -> jedis.srandmember(key)); } @Override public Long pfadd(String key, String... elements) { return execAndRetry(jedis -> jedis.pfadd(key, elements)); } @Override public long pfcount(String... keys) { return execAndRetry(jedis -> jedis.pfcount(keys)); } @Override public long pfcount(String key) { return execAndRetry(jedis -> jedis.pfcount(key)); } @Override public Double geodist(String key, String member1, String member2) { return execAndRetry(jedis -> jedis.geodist(key, member1, member2)); } @Override public Long zrem(String key, String... members) { return execAndRetry(jedis -> jedis.zrem(key, members)); } @Override public Set zrangeByScore(String key, double min, double max) { return execAndRetry(jedis -> jedis.zrangeByScore(key, min, max)); } @Override public Set zrangeByScore(String key, String min, String max) { return execAndRetry(jedis -> jedis.zrangeByScore(key, min, max)); } @Override public Set zrangeByScore(String key, double min, double max, int offset, int count) { return execAndRetry(jedis -> jedis.zrangeByScore(key, min, max, offset, count)); } @Override public Set zrangeByScore(String key, String min, String max, int offset, int count) { return execAndRetry(jedis -> jedis.zrangeByScore(key, min, max, offset, count)); } @Override public List geohash(String key, String... members) { return execAndRetry(jedis -> jedis.geohash(key, members)); } @Override public List mget(String... keys) { return execAndRetry(jedis -> jedis.mget(keys)); } @Override public Long zcard(String key) { return execAndRetry(jedis -> jedis.zcard(key)); } @Override public String rpoplpush(String srckey, String dstkey) { return execAndRetry(jedis -> jedis.rpoplpush(srckey, dstkey)); } @Override public Long smove(String srckey, String dstkey, String member) { return execAndRetry(jedis -> jedis.smove(srckey, dstkey, member)); } @Override public Object evalsha(String sha1, int keyCount, String... params) { return execAndRetry(jedis -> jedis.evalsha(sha1, keyCount, params)); } @Override public Object evalsha(String sha1, String sampleKey) { return execAndRetry(jedis -> jedis.evalsha(sha1)); } @Override public Object evalsha(String sha1, List keys, List args) { return execAndRetry(jedis -> jedis.evalsha(sha1, keys, args)); } @Override public List bitfield(String key, String... arguments) { return execAndRetry(jedis -> jedis.bitfield(key, arguments)); } @Override public Long sunionstore(String dstkey, String... keys) { return execAndRetry(jedis -> jedis.sunionstore(dstkey, keys)); } @Override public Long lpushx(String key, String... string) { return execAndRetry(jedis -> jedis.lpushx(key, string)); } @Override public String echo(String string) { return execAndRetry(jedis -> jedis.echo(string)); } @Override public Set zrevrangeByScore(String key, double max, double min) { return execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min)); } @Override public Set zrevrangeByScore(String key, double max, double min, int offset, int count) { return execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min, offset, count)); } @Override public Set zrevrangeByScore(String key, String max, String min, int offset, int count) { return execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min, offset, count)); } @Override public Set zrevrangeByScore(String key, String max, String min) { return execAndRetry(jedis -> jedis.zrevrangeByScore(key, max, min)); } @Override public Long srem(String key, String... members) { return execAndRetry(jedis -> jedis.srem(key, members)); } @Override public Long zcount(String key, String min, String max) { return execAndRetry(jedis -> jedis.zcount(key, min, max)); } @Override public Long zcount(String key, double min, double max) { return execAndRetry(jedis -> jedis.zcount(key, min, max)); } @Override public Set zrangeByLex(String key, String min, String max, int offset, int count) { return execAndRetry(jedis -> jedis.zrangeByLex(key, min, max, offset, count)); } @Override public Set zrangeByLex(String key, String min, String max) { return execAndRetry(jedis -> jedis.zrangeByLex(key, min, max)); } @Override public Set sdiff(String... keys) { return execAndRetry(jedis -> jedis.sdiff(keys)); } @Override public Set sunion(String... keys) { return execAndRetry(jedis -> jedis.sunion(keys)); } @Override public Long scard(String key) { return execAndRetry(jedis -> jedis.scard(key)); } @Override public Long renamenx(String oldkey, String newkey) { return execAndRetry(jedis -> jedis.renamenx(oldkey, newkey)); } @Override public Long zadd(String key, double score, String member) { return execAndRetry(jedis -> jedis.zadd(key, score, member)); } @Override public Long zadd(String key, Map scoreMembers) { return execAndRetry(jedis -> jedis.zadd(key, scoreMembers)); } @Override public Double zscore(String key, String member) { return execAndRetry(jedis -> jedis.zscore(key, member)); } @Override public Long zinterstore(String dstkey, String... sets) { return execAndRetry(jedis -> jedis.zinterstore(dstkey, sets)); } @Override public Long zunionstore(String dstkey, String... sets) { return execAndRetry(jedis -> jedis.zunionstore(dstkey, sets)); } @Override public Long sinterstore(String dstkey, String... keys) { return execAndRetry(jedis -> jedis.sinterstore(dstkey, keys)); } @Override public String randomKey() { return execAndRetry(jedis -> jedis.randomKey()); } @Override public Long strlen(String key) { return execAndRetry(jedis -> jedis.strlen(key)); } @Override public Set zrevrangeByLex(String key, String max, String min, int offset, int count) { return execAndRetry(jedis -> jedis.zrevrangeByLex(key, max, min, offset, count)); } @Override public Set zrevrangeByLex(String key, String max, String min) { return execAndRetry(jedis -> jedis.zrevrangeByLex(key, max, min)); } @Override public String pfmerge(String destkey, String... sourcekeys) { return execAndRetry(jedis -> jedis.pfmerge(destkey, sourcekeys)); } @Override public Boolean sismember(String key, String member) { return execAndRetry(jedis -> jedis.sismember(key, member)); } @Override public Set zrevrange(String key, long start, long stop) { return execAndRetry(jedis -> jedis.zrevrange(key, start, stop)); } @Override public String watch(String... keys) { return execAndRetry(jedis -> jedis.watch(keys)); } @Override public Long publish(String channel, String message) { return execAndRetry(jedis -> jedis.publish(channel, message)); } @Override public Long bitcount(String key) { return execAndRetry(jedis -> jedis.bitcount(key)); } @Override public Long bitcount(String key, long start, long end) { return execAndRetry(jedis -> jedis.bitcount(key, start, end)); } @Override public Long zremrangeByLex(String key, String min, String max) { return execAndRetry(jedis -> jedis.zremrangeByLex(key, min, max)); } @Override public Long sdiffstore(String dstkey, String... keys) { return execAndRetry(jedis -> jedis.sdiffstore(dstkey, keys)); } @Override public Long move(String key, int dbIndex) { return execAndRetry(jedis -> jedis.move(key, dbIndex)); } @Override public Long geoadd(String key, double longitude, double latitude, String member) { return execAndRetry(jedis -> jedis.geoadd(key, longitude, latitude, member)); } @Override public Set smembers(String key) { return execAndRetry(jedis -> jedis.smembers(key)); } @Override public Set zrange(String key, long start, long stop) { return execAndRetry(jedis -> jedis.zrange(key, start, stop)); } @Override public Long zlexcount(String key, String min, String max) { return execAndRetry(jedis -> jedis.zlexcount(key, min, max)); } @Override public Long hstrlen(String key, String field) { return execAndRetry(jedis -> jedis.hstrlen(key, field)); } @Override public Long zremrangeByScore(String key, double min, double max) { return execAndRetry(jedis -> jedis.zremrangeByScore(key, min, max)); } @Override public Long zremrangeByScore(String key, String min, String max) { return execAndRetry(jedis -> jedis.zremrangeByScore(key, min, max)); } @Override public String brpoplpush(String source, String destination, int timeout) { return execAndRetry(jedis -> jedis.brpoplpush(source, destination, timeout)); } @Override public Long zremrangeByRank(String key, long start, long stop) { return execAndRetry(jedis -> jedis.zremrangeByRank(key, start, stop)); } @Override public Set sinter(String... keys) { return execAndRetry(jedis -> jedis.sinter(keys)); } @Override public String scriptLoad(String script) { return execAndRetry(jedis -> jedis.scriptLoad(script)); } @Override public Set spop(String key, long count) { return execAndRetry(jedis -> jedis.spop(key, count)); } @Override public String spop(String key) { return execAndRetry(jedis -> jedis.spop(key)); } @Override public List blpop(int timeout, String... keys) { return execAndRetry(jedis -> jedis.blpop(timeout, keys)); } @Override public List blpop(int timeout, String key) { return execAndRetry(jedis -> jedis.blpop(timeout, key)); } @Override public List blpop(String... args) { return execAndRetry(jedis -> jedis.blpop(args)); } @Override public String mset(String... keysvalues) { return execAndRetry(jedis -> jedis.mset(keysvalues)); } @Override public byte[] get(byte[] key) { return execAndRetry(jedis -> jedis.get(key)); } @Override public String type(byte[] key) { return execAndRetry(jedis -> jedis.type(key)); } @Override public String set(byte[] key, byte[] value, byte[] nxxx, byte[] expx, long time) { return execAndRetry( jedis -> jedis.set(key, value, Redis3Kit.createSetParams(new String(nxxx, StandardCharsets.UTF_8), new String(expx, StandardCharsets.UTF_8), time))); } @Override public String set(byte[] key, byte[] value) { return execAndRetry(jedis -> jedis.set(key, value)); } @Override public Boolean exists(byte[] key) { return execAndRetry(jedis -> jedis.exists(key)); } @Override public List hvals(byte[] key) { return execAndRetry(jedis -> jedis.hvals(key)); } @Override public List hmget(byte[] key, byte[]... fields) { return execAndRetry(jedis -> jedis.hmget(key, fields)); } @Override public Map hgetAll(byte[] key) { return execAndRetry(jedis -> jedis.hgetAll(key)); } @Override public Long rpush(byte[] key, byte[]... strings) { return execAndRetry(jedis -> jedis.rpush(key, strings)); } @Override public Long llen(byte[] key) { return execAndRetry(jedis -> jedis.llen(key)); } @Override public byte[] lindex(byte[] key, long index) { return execAndRetry(jedis -> jedis.lindex(key, index)); } @Override public Long lpush(byte[] key, byte[]... strings) { return execAndRetry(jedis -> jedis.lpush(key, strings)); } @Override public Long lrem(byte[] key, long count, byte[] value) { return execAndRetry(jedis -> jedis.lrem(key, count, value)); } @Override public byte[] lpop(byte[] key) { return execAndRetry(jedis -> jedis.lpop(key)); } @Override public String lset(byte[] key, long index, byte[] value) { return execAndRetry(jedis -> jedis.lset(key, index, value)); } @Override public byte[] rpop(byte[] key) { return execAndRetry(jedis -> jedis.rpop(key)); } @Override public Long del(byte[] key) { return execAndRetry(jedis -> jedis.del(key)); } @Override public Long hdel(byte[] key, byte[]... fields) { return execAndRetry(jedis -> jedis.hdel(key, fields)); } @Override public String ltrim(byte[] key, long start, long stop) { return execAndRetry(jedis -> jedis.ltrim(key, start, stop)); } @Override public List lrange(byte[] key, long start, long stop) { return execAndRetry(jedis -> jedis.lrange(key, start, stop)); } @Override public Long hsetnx(byte[] key, byte[] field, byte[] value) { return execAndRetry(jedis -> jedis.hsetnx(key, field, value)); } @Override public Set hkeys(byte[] key) { return execAndRetry(jedis -> jedis.hkeys(key)); } @Override public Long hset(byte[] key, Map hash) { return execAndRetry(jedis -> jedis.hset(key, hash)); } @Override public Long hset(byte[] key, byte[] field, byte[] value) { return execAndRetry(jedis -> jedis.hset(key, field, value)); } @Override public byte[] hget(byte[] key, byte[] field) { return execAndRetry(jedis -> jedis.hget(key, field)); } @Override public String hmset(byte[] key, Map hash) { return execAndRetry(jedis -> jedis.hmset(key, hash)); } @Override public Boolean hexists(byte[] key, byte[] field) { return execAndRetry(jedis -> jedis.hexists(key, field)); } @Override public Long pexpire(byte[] key, long milliseconds) { return execAndRetry(jedis -> jedis.pexpire(key, milliseconds)); } @Override public Long pttl(byte[] key) { return execAndRetry(jedis -> jedis.pttl(key)); } @Override public Long setrange(byte[] key, long offset, byte[] value) { return execAndRetry(jedis -> jedis.setrange(key, offset, value)); } @Override public byte[] getrange(byte[] key, long startOffset, long endOffset) { return execAndRetry(jedis -> jedis.getrange(key, startOffset, endOffset)); } @Override public String unwatch() { return execAndRetry(jedis -> jedis.unwatch()); } @Override public void subscribe(JedisPubSub jedisPubSub, String... channels) { execAndRetry(jedis -> { jedis.subscribe(jedisPubSub, channels); return "OK"; }); } @Override public void psubscribe(JedisPubSub jedisPubSub, String... patterns) { execAndRetry(jedis -> { jedis.psubscribe(jedisPubSub, patterns); return "OK"; }); } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/SentinelJedisExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import org.yx.redis.RedisConfig; import org.yx.redis.RedisType; import redis.clients.jedis.Jedis; import redis.clients.jedis.util.Pool; public class SentinelJedisExecutor extends AbstractJedisExecutor { public SentinelJedisExecutor(RedisConfig config, Pool pool) { super(config, pool); } @Override public RedisType redisType() { return RedisType.SENTINEL; } } ================================================ FILE: sumk-framework/src/main/java/org/yx/redis/v3/SingleJedisExecutor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.redis.v3; import org.yx.log.Logs; import org.yx.redis.Checkable; import org.yx.redis.RedisConfig; import org.yx.redis.RedisType; import redis.clients.jedis.Jedis; import redis.clients.jedis.util.Pool; public class SingleJedisExecutor extends AbstractJedisExecutor implements Checkable { public SingleJedisExecutor(RedisConfig config, Pool pool) { super(config, pool); } @Override public boolean aliveCheck() { Logs.redis().trace("{}-{} begin alive check", this, this.hashCode()); final String PONE = "PONG"; for (int i = 0; i < 3; i++) { try (Jedis jedis = jedis()) { String ret = jedis.ping(); if (PONE.equalsIgnoreCase(ret)) { this.disConnected = false; return true; } Logs.redis().warn("{} answer {}", this, ret); } catch (Exception e) { if (!isConnectException(e)) { Logs.redis().error(e.getMessage(), e); if (!this.disConnected) { return false; } } } } this.disConnected = true; return false; } @Override public RedisType redisType() { return RedisType.SIMPLE; } } ================================================ FILE: sumk-framework/src/main/resources/META-INF/lua_del ================================================ if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end ================================================ FILE: sumk-framework/src/main/resources/META-INF/sumk.factories ================================================ sumk.ioc.bean=org.yx.common.listener.EventBusFactory,\ org.yx.common.validate.ComplexParamValidator,org.yx.common.validate.SimpleParamValidator,\ org.yx.redis.RedisPlugin,org.yx.redis.v3.Redis3Factory sumk.ioc.boot.watcher=org.yx.bean.BeanAssemblerBootWatcher sumk.ioc.optional=org.yx.redis.* ================================================ FILE: sumk-http/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: sumk-http/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-http com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots com.github.youtongluan sumk-framework ${project.version} org.eclipse.jetty jetty-servlet junit junit test ================================================ FILE: sumk-http/src/main/java/org/yx/annotation/http/SumkFilter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.http; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.servlet.DispatcherType; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SumkFilter { String name() default ""; /** * @return 要拦截的路径 */ String[] path(); DispatcherType[] dispatcherType() default {}; /** * @return true表示往后面添加 */ boolean isMatchAfter() default true; boolean asyncSupported() default true; } ================================================ FILE: sumk-http/src/main/java/org/yx/annotation/http/SumkServlet.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.http; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SumkServlet { /** * @return servlet的名字 */ String name() default ""; /** * @return 访问路径 */ String[] path(); int loadOnStartup() default -1; boolean asyncSupported() default false; String appKey() default ""; } ================================================ FILE: sumk-http/src/main/java/org/yx/annotation/http/Upload.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.http; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 与@Web一起使用,表示该请求是multipart/form-data类型,一般而言就是文件上传。
* 其中名字为paramName()的那个part,会解析成方法的参数 */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Upload { String paramName() default "param"; } ================================================ FILE: sumk-http/src/main/java/org/yx/annotation/http/Web.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.http; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.yx.http.MessageType; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Web { /** * @return 服务名称,如果为空,就根据方法名获取 */ String value() default ""; String cnName() default ""; /** * 本功能默认关闭。需要在启动的时候设置sumk.http.login.enable=1才能开启。 * 开启本功能后,session中的userId会在整个调用链路上传递 * * @return 如果已经开启了,并且该值为true,框架会校验当前用户是否存在session,以及是否过期 */ boolean requireLogin() default true; MessageType requestType() default MessageType.DEFAULT; /** * 为了调试方便,可以在启动的时候设置sumk.http.sign.enable=0来禁用它 * * @return 如果设为true,框架会校验请求的签名 */ boolean sign() default false; MessageType responseType() default MessageType.DEFAULT; /** * 留给开发者做权限分组等,框架本身没有实际应用 * * @return 标签(或分组)列表 */ String[] tags() default {}; int toplimit() default 0; String[] method() default {}; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/HttpEncryptor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import org.yx.http.handler.WebContext; public interface HttpEncryptor { byte[] encrypt(byte[] data, WebContext ctx) throws Exception; byte[] decrypt(byte[] data, WebContext ctx) throws Exception; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/HttpErrorCode.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; /** * 3位数的错误码,为系统所保留,应用系统的错误码,要避开这个区间。客户端可以对这些异常码做额外处理,尤其是登录相关部分
* 用户异常码推荐4位数,BizException中的code会对应http中json的异常码。
* 这个错误码对应于http请求的返回码550时的异常码。它是body里的错误码(json格式).
* json有2个默认字段code、message,其中code为int类型
*/ public interface HttpErrorCode { /** * 线程数溢出 */ int THREAD_THRESHOLD_OVER = 900; /** * 登录失败 */ int LOGINFAILED = 901; /** * session过期或冲突 */ int SESSION_ERROR = 902; /** * 用户在其它地方登录了 */ int LOGIN_AGAIN = 903; /** * 熔断 */ int FUSING = 905; /** * 参数验证错误 */ int VALIDATE_ERROR = 910; int BODY_TOO_BIG = 911; int SIGN_EMPTY = 912; int SIGN_MISTAKE = 913; int FILE_MISS = 914; /** * sumk.http.upload.enable被设置为false,@upload注解被禁用 */ int UPLOAD_DISABLED = 930; int UPLOAD_NOT_MULTI_TYPE = 931; int UPLOAD_ANNOTATION_MISS = 932; /** * 加密用的key没有找到 */ int SESSION_KEY_NOT_FOUND = 940; /** * 请求处理出错 */ int HANDLE_ERROR = 950; /** * 请求格式不正确 */ int ACT_FORMAT_ERROR = 951; /** * 数据格式不正确 */ int DATA_FORMAT_ERROR = 952; /** * 框架处理的时候,出现了异常。这个异常可能是框架的,也可能是数据原因 */ int FRAMEWORK_ERROR = 953; /** * 接口没有定义,类似于404 */ int ACT_NOT_FOUND = 954; /** * 不支持该http方法,比如GET */ int METHOD_UNSUPPORT = 960; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/HttpHeaderName.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import org.yx.conf.AppInfo; public final class HttpHeaderName { private static String sessionId; private static String userFlag; public static void init() { sessionId = AppInfo.get("sumk.http.name.sessionId", "sid"); userFlag = AppInfo.get("sumk.http.name.userFlag", "userFlag"); } public static String sessionId() { return sessionId; } public static String userFlag() { return userFlag; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/HttpJson.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import java.util.Objects; import org.yx.common.json.GsonHelper; import org.yx.common.json.GsonOperator; import org.yx.common.json.JsonOperator; import org.yx.common.json.ServerJsonExclusionStrategy; import org.yx.conf.AppInfo; import com.google.gson.GsonBuilder; import com.google.gson.LongSerializationPolicy; public final class HttpJson { private static JsonOperator operator = new GsonOperator(gsonBuilder().create()); private static GsonBuilder gsonBuilder() { GsonBuilder gb = GsonHelper.builder("sumk.http"); if (AppInfo.getBoolean("sumk.http.json.long2String", true)) { gb.setLongSerializationPolicy(LongSerializationPolicy.STRING); } return ServerJsonExclusionStrategy.addServerExclusionStrategy(gb); } public static JsonOperator operator() { return operator; } public static void setOperator(JsonOperator operator) { HttpJson.operator = Objects.requireNonNull(operator); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/HttpPlugin.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.slf4j.Logger; import org.yx.annotation.Bean; import org.yx.base.Lifecycle; import org.yx.base.context.AppContext; import org.yx.bean.IOC; import org.yx.bean.InnerIOC; import org.yx.bean.Plugin; import org.yx.common.StringEntity; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.exception.SumkException; import org.yx.http.act.HttpActionNode; import org.yx.http.act.HttpActions; import org.yx.http.handler.HttpHandler; import org.yx.http.handler.HttpHandlerChain; import org.yx.http.handler.RestType; import org.yx.http.invoke.WebHandler; import org.yx.http.kit.HttpSettings; import org.yx.http.start.WebAnnotationResolver; import org.yx.http.user.WebSessions; import org.yx.log.Logs; import org.yx.main.StartConstants; import org.yx.main.SumkServer; import org.yx.util.ExceptionUtil; import org.yx.util.Loader; import org.yx.util.StringUtil; @Bean public class HttpPlugin implements Plugin { protected Lifecycle server; @Override public void stop() { if (this.server != null) { server.stop(); this.server = null; } } @Override public int order() { return 10100; } protected void resolveWebAnnotation(Collection beans) { WebAnnotationResolver factory = new WebAnnotationResolver(); try { List> infos = new ArrayList<>(100); for (Object bean : beans) { List> acts = factory.resolve(bean); if (acts != null && acts.size() > 0) { infos.addAll(acts); } } HttpActions.init(infos); } catch (Exception e) { throw SumkException.wrap(e); } } @Override public void startAsync() { int port = SumkServer.httpPort(); if (!this.isHttpEnable() || port < 1) { return; } try { HttpHeaderName.init(); HttpSettings.init(); resolveWebAnnotation(InnerIOC.beans()); WebHandler.init(); this.buildHttpHandlers(); this.initServer(port); } catch (Exception e) { throw ExceptionUtil.toRuntimeException(e); } } protected void initServer(int port) throws Exception { String nojetty = StartConstants.EMBED_WEBSERVER_DISABLE; if (AppContext.inst().get(nojetty) != null || AppInfo.getBoolean(nojetty, false)) { return; } String httpServerClass = StringUtil.isEmpty(AppInfo.get(Const.KEY_STORE_PATH)) ? "JettyServer" : "JettyHttpsServer"; String hs = AppInfo.get("sumk.http.starter.class", "org.yx.http.start." + httpServerClass); Class httpClz = Loader.loadClass(hs); Constructor c = httpClz.getConstructor(int.class); this.server = (Lifecycle) c.newInstance(port); } protected void buildHttpHandlers() { List handlers = IOC.getBeans(HttpHandler.class); List restHandlers = new ArrayList<>(handlers.size()); List uploadHandlers = new ArrayList<>(handlers.size()); for (HttpHandler h : handlers) { if (h.supportRestType(RestType.TEXT)) { restHandlers.add(h); } if (h.supportRestType(RestType.MULTI_PART)) { uploadHandlers.add(h); } } HttpHandlerChain.rest.setHandlers(restHandlers); Logger logger = Logs.http(); if (logger.isDebugEnabled()) { logger.debug("rest handlers:{}", this.buildString(restHandlers)); } if (HttpSettings.isUploadEnable()) { HttpHandlerChain.multipart.setHandlers(uploadHandlers); if (logger.isDebugEnabled()) { logger.debug("upload handlers:{}", this.buildString(uploadHandlers)); } } } protected boolean isHttpEnable() { if (!SumkServer.isHttpEnable()) { return false; } try { Loader.loadClass("javax.servlet.http.HttpServlet"); } catch (Exception e) { Logs.http().warn("javax-servlet-api-**.jar is not imported"); return false; } return true; } @Override public void afterStarted() { Lifecycle server = this.server; if (!SumkServer.isHttpEnable() || server == null) { return; } WebSessions.initSession(); server.start(); } protected String buildString(List hs) { StringBuilder sb = new StringBuilder(); for (HttpHandler h : hs) { sb.append(" ").append(h.getClass().getSimpleName()).append("(").append(h.order()).append(")"); } return sb.toString(); } public Lifecycle getServer() { return server; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/MessageType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; public enum MessageType { DEFAULT(false, false), PLAIN(false, false), BASE64(false, true), ENCRYPT_BASE64(true, true), ENCRYPT(true, false); private final boolean encrypt; private final boolean base64; private MessageType(boolean encrypt, boolean base64) { this.encrypt = encrypt; this.base64 = base64; } public boolean isEncrypt() { return this.encrypt; } public boolean isBase64() { return this.base64; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/Signer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import javax.servlet.http.HttpServletRequest; @FunctionalInterface public interface Signer { String sign(byte[] bs, HttpServletRequest httpServletRequest) throws Exception; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/WebFilter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import java.util.Objects; import org.yx.base.Ordered; import org.yx.exception.SumkException; import org.yx.http.handler.WebContext; public abstract class WebFilter implements Ordered { private WebFilter next; public final void setNext(WebFilter next) { if (this.next != null) { throw new SumkException(7682343, "next已经赋值了,它是" + this.next); } this.next = Objects.requireNonNull(next); } protected final Object callNextFilter(WebContext ctx) throws Throwable { return this.next.doFilter(ctx); } /** * 本方法只需要实现,不需要去调用任何接口的本方法。
* 这个方法里一般需要在里面调用callNextFilter(ctx)。 * * @param ctx web请求的上下文 * @return 返回给请求端的对象 * @throws Throwable 如果抛出了异常,就会返回类似于“未知异常”这种提示信息 */ public abstract Object doFilter(WebContext ctx) throws Throwable; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/WebUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http; import java.nio.charset.Charset; import java.util.List; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.yx.annotation.doc.NotNull; import org.yx.http.act.HttpActions; import org.yx.http.handler.MultipartHolder; import org.yx.http.handler.MultipartItem; import org.yx.http.handler.WebContext; import org.yx.http.kit.HttpKit; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.kit.LocalWebContext; import org.yx.http.user.SessionObject; import org.yx.http.user.WebSessions; import org.yx.log.Logs; /** * 注意:本工具类不能在自定义的servlet中调用,比如login */ public final class WebUtil { public static T getUserObject(Class clz) { return WebSessions.getUserObject(getSessionId(), clz); } /** * 移除session内容,也就是logout */ public static void removeUserObject() { WebSessions.remove(getSessionId()); } public static WebContext context() { return LocalWebContext.getCtx(); } /** * 获取http请求中的HttpServletRequest对象 * * @return HttpServletRequest对象 */ public static HttpServletRequest getHttpRequest() { WebContext ctx = context(); if (ctx != null) { return ctx.httpRequest(); } return null; } /** * 从request对象获取sessionId * * @return sessionId */ public static String getSessionId() { return fromHeaderOrCookieOrParamter(getHttpRequest(), HttpHeaderName.sessionId()); } private static String fromHeaderOrCookie(HttpServletRequest req, String name, boolean useCookie) { String value = req.getHeader(name); if (value != null && value.length() > 0) { return value; } if (!useCookie) { return null; } Cookie[] cookies = req.getCookies(); if (cookies == null || cookies.length == 0) { return null; } for (Cookie cookie : cookies) { if (name.equals(cookie.getName())) { return cookie.getValue(); } } return null; } private static String getValueFromRequest(HttpServletRequest req, @NotNull String name, boolean useCookie) { if (req == null) { Logs.http().info("request is null"); return null; } Object attr = req.getAttribute("sumk.".concat(name)); if (attr != null && attr.getClass() == String.class) { return (String) attr; } String v = fromHeaderOrCookie(req, name, useCookie); if (v != null) { return v; } return req.getParameter(name); } public static String fromHeaderOrCookieOrParamter(HttpServletRequest req, String name) { return getValueFromRequest(req, name, true); } public static String fromHeaderOrParamter(HttpServletRequest req, String name) { return getValueFromRequest(req, name, false); } public static String getUserFlag(HttpServletRequest req) { return fromHeaderOrCookieOrParamter(req, HttpHeaderName.userFlag()); } public static Charset getCharset(HttpServletRequest req) { return InnerHttpUtil.charset(req); } public static String getUserId() { SessionObject obj = getUserObject(SessionObject.class); if (obj == null) { return null; } return obj.getUserId(); } /** * 用这个方法获取上传项,仅用于@upload修饰的接口 * * @return 除了参数外所有的multipart */ public static List getMultiParts() { return MultipartHolder.get(); } /** * 根据name获取对应的MultipartItem,仅用于@upload修饰的接口 * * @param name MultipartItem对应的名称,注意该名称是part的名称,而不是文件名 * @return 对应的MultipartItem,如果不存在就返回null */ public static MultipartItem getPart(String name) { List list = MultipartHolder.get(); if (list == null || list.isEmpty()) { return null; } for (MultipartItem p : list) { if (name.equals(p.getName())) { return p; } } return null; } public static HttpKit getKit() { return InnerHttpUtil.getKit(); } public static void setKit(HttpKit kit) { InnerHttpUtil.setKit(kit); } public static byte[] getSessionEncryptKey() { String sessionId = WebUtil.getSessionId(); if (sessionId == null) { return null; } return WebSessions.loadUserSession().getEncryptKey(sessionId); } /** * @return 前缀匹配的方式里,获取剩余的url */ public static String getUrlLeft() { WebContext ctx = context(); String action = ctx.actionInfo().formalName(); if (!action.endsWith(HttpActions.PREFIX_MATCH_ENDING)) { return null; } String p = ctx.httpRequest().getPathInfo(); if (p == null) { return null; } p = HttpActions.formatActionName(p); if (p.length() < action.length()) { return ""; } return p.substring(action.length() - 1); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/AbstractActionInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; import java.util.Objects; public abstract class AbstractActionInfo implements HttpActionInfo { protected final String rawAct; protected final HttpActionNode node; /** * 如果要支持url中的参数,可以考虑从这里入手 */ protected final String formalName; public AbstractActionInfo(String rawAct, HttpActionNode node, String formatedName) { this.rawAct = Objects.requireNonNull(rawAct); this.node = Objects.requireNonNull(node); this.formalName = Objects.requireNonNull(formatedName); } public String rawAct() { return rawAct; } public HttpActionNode node() { return node; } /** * @return 解析后正式的名字 */ public String formalName() { return formalName; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/ActNameResolver.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; import java.util.List; import java.util.Objects; import java.util.function.Function; import org.yx.util.StringUtil; public class ActNameResolver implements Function { private final boolean ignoreCase; public ActNameResolver(boolean ignoreCase) { this.ignoreCase = ignoreCase; } @Override public String apply(String name) { String act = this.solve(Objects.requireNonNull(name)); if (ignoreCase) { return act.toLowerCase(); } return act; } private String solve(String name) { List list = StringUtil.splitAndTrim(name, "/", "\\"); return String.join("/", list); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/HttpActionInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; public interface HttpActionInfo extends Comparable { /** * 原始路径 * * @return @Web里定义的原始路径 */ String rawAct(); HttpActionNode node(); /** * @return 解析后正式的名字 */ String formalName(); /** * 是否接受当前请求。有些实现类为了性能考虑,不对act做校验。 * * @param act 格式化后的接口名,不为null * @param method 请求的http方法,不为null * @return true表示接受当前请求 */ boolean match(String act, String method); default int compareTo(HttpActionInfo o) { return this.formalName().compareTo(o.formalName()); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/HttpActionNode.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; import java.lang.reflect.Method; import java.util.List; import java.util.Objects; import org.yx.bean.aop.asm.MethodPojo; import org.yx.bean.aop.asm.ParamPojo; import org.yx.bean.aop.context.CalleeNode; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.exception.BizException; import org.yx.exception.SumkException; import org.yx.http.HttpErrorCode; import org.yx.http.HttpJson; import org.yx.http.MessageType; import org.yx.http.kit.HttpSettings; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.spec.HttpSpecs; import org.yx.http.spec.UploadSpec; import org.yx.http.spec.WebSpec; import org.yx.log.Logs; import org.yx.util.CollectionUtil; public final class HttpActionNode extends CalleeNode { private final boolean requireLogin; private final MessageType requestType; private final boolean sign; private final MessageType responseType; private final List httpMethod; private final List tags; private final String cnName; private final UploadSpec upload; public UploadSpec upload() { return this.upload; } public ParamPojo buildParamPojo(Object reqData) throws Exception { if (this.params.paramLength() == 0 || reqData == null) { return createEmptyParamObj(); } Class argClz = this.params.paramClz(); if (reqData.getClass() != String.class) { if (argClz.isInstance(reqData)) { return argClz.cast(reqData); } throw new SumkException(1245464, "http argument " + reqData.getClass().getName() + " is not String"); } String data = (String) reqData; if (data.isEmpty()) { return createEmptyParamObj(); } try { return HttpJson.operator().fromJson(data, argClz); } catch (Exception e) { Logs.http().warn("json解析异常", e); throw BizException.create(HttpErrorCode.DATA_FORMAT_ERROR, "数据格式错误"); } } private MessageType getMessageType(MessageType configed, String key) { if (configed != MessageType.DEFAULT) { return configed; } return InnerHttpUtil.parseMessageType(AppInfo.get(key)); } private List httpMethod(WebSpec web) { String[] m = web.method(); if (m.length > 0) { return CollectionUtil.unmodifyList(m); } return HttpSettings.defaultHttpMethods(); } public HttpActionNode(Object obj, Method method, MethodPojo argClzInfo, WebSpec action) { super(obj, method, argClzInfo, Objects.requireNonNull(action).toplimit() > 0 ? action.toplimit() : AppInfo.getInt("sumk.http.toplimit.default", Const.DEFAULT_TOPLIMIT)); this.cnName = action.cnName(); this.httpMethod = httpMethod(action); this.requestType = this.params.paramLength() == 0 ? MessageType.PLAIN : getMessageType(action.requestType(), "sumk.http.request.type"); this.responseType = getMessageType(action.responseType(), "sumk.http.response.type"); this.requireLogin = (action.requireLogin() && AppInfo.getBoolean("sumk.http.login.enable", false)) || action.requestType().isEncrypt() || action.responseType().isEncrypt(); this.sign = this.params.paramLength() != 0 && action.sign() && AppInfo.getBoolean("sumk.http.sign.enable", true); this.tags = CollectionUtil.unmodifyList(action.tags()); this.upload = HttpSpecs.extractUpload(obj, method); } /** * 返回值受@Web、sumk.http.login.enable配置、requestType、responseType影响。
* 如果requestType、responseType中有一个为需要加密,它就返回true。 * * @return true表示接口需要登录后才能访问 */ public boolean requireLogin() { return requireLogin; } /** * 如果没有入参,那么它一定是PLAIN类型 * * @return 请求体类型 */ public MessageType requestType() { return requestType; } /** * 如果没有入参,那么它就一定是false * * @return 参数签名 */ public boolean sign() { return sign; } public MessageType responseType() { return responseType; } public boolean acceptMethod(String httpMethod) { return this.httpMethod.contains(httpMethod); } public List tags() { return this.tags; } public List methods() { return this.httpMethod; } public String cnName() { return cnName; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/HttpActions.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.common.StringEntity; import org.yx.common.action.ActInfoUtil; import org.yx.common.locale.I18n; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.MessageType; import org.yx.http.select.DefaultHttpActionSelector; import org.yx.http.select.HttpActionSelector; import org.yx.log.Logs; import org.yx.main.SumkServer; import org.yx.util.StringUtil; public final class HttpActions { private static HttpActionSelector selector; private static Function nameResolver; private static Predicate fusing = BooleanMatcher.FALSE; public static final String PREFIX_MATCH_ENDING = "/*"; public static synchronized void init(List> infos) { if (nameResolver == null) { nameResolver = new ActNameResolver(AppInfo.getBoolean("sumk.http.act.ignorecase", false)); } if (selector == null) { selector = new DefaultHttpActionSelector(); } if (infos == null || infos.isEmpty()) { return; } selector.init(infos, nameResolver); AppInfo.addObserver(info -> { String fusings = AppInfo.getLatin("sumk.http.fusing", null); if (fusings == null) { HttpActions.fusing = BooleanMatcher.FALSE; return; } Set set = new HashSet<>(); for (String f : StringUtil.splitAndTrim(fusings, Const.COMMA, Const.SEMICOLON)) { String act = formatActionName(f); if (act != null && act.length() > 0) { set.add(act); } } HttpActions.fusing = Matchers.createWildcardMatcher(set, 1); }); } public static HttpActionInfo getHttpInfo(String requestAct, String method) { String usedAct = formatActionName(requestAct); if (usedAct == null || usedAct.isEmpty()) { Logs.http().error("act is empty for {}", requestAct); throw BizException.create(HttpErrorCode.ACT_FORMAT_ERROR, I18n.get("sumk.http.error.actformat", "{0}请求格式不正确", requestAct)); } if (fusing.test(usedAct)) { throw BizException.create(HttpErrorCode.FUSING, I18n.get("sumk.http.error.fusing", "{0}请求被熔断", usedAct)); } return selector.getHttpInfo(usedAct, method); } public static String formatActionName(String rawName) { return nameResolver.apply(rawName); } public static Collection actions() { return selector.actions(); } public static List> infos(boolean full) { if (!SumkServer.isHttpEnable()) { return Collections.emptyList(); } List infos = new ArrayList<>(actions()); List> ret = new ArrayList<>(infos.size()); for (HttpActionInfo info : infos) { HttpActionNode http = info.node(); Map map = full ? ActInfoUtil.fullInfoMap(info.rawAct(), http) : ActInfoUtil.simpleInfoMap(info.rawAct(), http); ret.add(map); map.put("formalName", info.formalName()); map.put("cnName", http.cnName()); map.put("requireLogin", http.requireLogin()); if (http.requestType() != MessageType.PLAIN) { map.put("requestEncrypt", http.requestType()); } if (http.responseType() != MessageType.PLAIN) { map.put("responseEncrypt", http.responseType()); } if (http.sign()) { map.put("sign", http.sign()); } if (http.toplimit() != Const.DEFAULT_TOPLIMIT) { map.put("toplimit", http.toplimit()); } if (http.tags().size() > 0) { map.put("tags", http.tags()); } map.put("httpMethod", http.methods()); if (StringUtil.isNotEmpty(http.comment())) { map.put("comment", http.comment()); } if (http.upload() != null) { map.put("upload", true); } } return ret; } public static HttpActionSelector getSelector() { return selector; } public static void setSelector(HttpActionSelector selector) { HttpActions.selector = Objects.requireNonNull(selector); } public static void setNameResolver(Function nameResolver) { HttpActions.nameResolver = Objects.requireNonNull(nameResolver); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/IgnoreNameActionInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; /** * 为了性能考虑,不对请求名做校验 */ public class IgnoreNameActionInfo extends AbstractActionInfo { public IgnoreNameActionInfo(String rawAct, HttpActionNode node, String formatedName) { super(rawAct, node, formatedName); } @Override public boolean match(String act, String method) { return node.acceptMethod(method); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/act/PrefixMappingActionInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.act; public class PrefixMappingActionInfo extends AbstractActionInfo { protected final String urlStart; public PrefixMappingActionInfo(String rawAct, HttpActionNode node, String formatedName, String urlStart) { super(rawAct, node, formatedName); this.urlStart = urlStart; } public String getUrlStart() { return urlStart; } @Override public boolean match(String act, String method) { return act.startsWith(this.urlStart) && node.acceptMethod(method); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/AbstractHttpHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; public abstract class AbstractHttpHandler implements HttpHandler { protected void setData(WebContext ctx, Object data) { ctx.data(data); } protected void setResult(WebContext ctx, Object result) { ctx.result(result, true); } protected void setResultNoCache(WebContext ctx, Object result) { ctx.result(result, false); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/Base64DecodeHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.common.util.S; import org.yx.http.kit.HttpSettings; @Bean public class Base64DecodeHandler implements HttpHandler { @Override public int order() { return 1400; } @Override public void handle(WebContext ctx) throws Exception { if (!ctx.node().requestType().isBase64() || HttpSettings.allowPlain(ctx.httpRequest())) { return; } byte[] bs = ctx.getDataInByteArray(); if (bs == null) { return; } byte[] data = S.base64().decode(bs); ctx.data(data); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/Base64EncodeHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.common.util.S; import org.yx.http.kit.HttpSettings; @Bean public class Base64EncodeHandler implements HttpHandler { @Override public int order() { return 2400; } @Override public void handle(WebContext ctx) throws Exception { if (!ctx.node().responseType().isBase64() || HttpSettings.allowPlain(ctx.httpRequest())) { return; } byte[] bs = (byte[]) ctx.result(); byte[] data = S.base64().encode(bs); ctx.result(data, false); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/DecryptHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.http.kit.HttpCiphers; import org.yx.http.kit.HttpSettings; @Bean public class DecryptHandler implements HttpHandler { @Override public int order() { return 1500; } @Override public void handle(WebContext ctx) throws Exception { if (!ctx.node().requestType().isEncrypt() || HttpSettings.allowPlain(ctx.httpRequest())) { return; } byte[] bs = ctx.getDataInByteArray(); if (bs == null) { return; } byte[] data = HttpCiphers.getEncryptor().decrypt(bs, ctx); ctx.data(data); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/EncryptHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.http.kit.HttpCiphers; import org.yx.http.kit.HttpSettings; @Bean public class EncryptHandler implements HttpHandler { @Override public int order() { return 2300; } @Override public void handle(WebContext ctx) throws Exception { if (!ctx.node().responseType().isEncrypt() || HttpSettings.allowPlain(ctx.httpRequest())) { return; } byte[] bs = (byte[]) ctx.result(); byte[] data = HttpCiphers.getEncryptor().encrypt(bs, ctx); ctx.result(data, false); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/HttpHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.base.Ordered; public interface HttpHandler extends Ordered { void handle(WebContext ctx) throws Throwable; default boolean supportRestType(String type) { return true; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/HttpHandlerChain.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import java.util.List; import org.slf4j.Logger; import org.yx.log.Log; public final class HttpHandlerChain { public static final HttpHandlerChain rest = new HttpHandlerChain(); public static final HttpHandlerChain multipart = new HttpHandlerChain(); private Logger LOG = Log.get("sumk.http.chain"); private HttpHandler[] handlers; public void setHandlers(List handlers) { this.handlers = handlers.toArray(new HttpHandler[handlers.size()]); } public void handle(WebContext ctx) throws Throwable { for (HttpHandler h : this.handlers) { if (h.order() < ctx.getLowestOrder()) { continue; } if (LOG.isTraceEnabled()) { if (ctx.data() instanceof String) { String s = ((String) ctx.data()); LOG.trace("{} - {} with data:{}", ctx.rawAct(), h.getClass().getSimpleName(), s); } } h.handle(ctx); } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/InvokeHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.http.invoke.WebHandler; @Bean public class InvokeHandler implements HttpHandler { @Override public int order() { return 2000; } @Override public void handle(WebContext ctx) throws Throwable { ctx.result(WebHandler.handle(ctx), true); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/MultiItemImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.Objects; import javax.servlet.http.Part; public class MultiItemImpl implements MultipartItem { private final Part part; public MultiItemImpl(Part part) { this.part = Objects.requireNonNull(part); } @Override public String getName() { return part.getName(); } @Override public long getSize() { return part.getSize(); } @Override public InputStream getInputStream() throws IOException { return part.getInputStream(); } @Override public String getSubmittedFileName() { return part.getSubmittedFileName(); } @Override public String getHeader(String name) { return part.getHeader(name); } @Override public Collection getHeaders(String name) { return part.getHeaders(name); } @Override public Collection getHeaderNames() { return part.getHeaderNames(); } @Override public String getContentType() { return part.getContentType(); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/MultipartHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.servlet.http.Part; import org.yx.annotation.Bean; import org.yx.common.locale.I18n; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.spec.UploadSpec; import org.yx.log.Logs; @Bean public class MultipartHandler implements HttpHandler { @Override public boolean supportRestType(String type) { return RestType.MULTI_PART.equals(type); } @Override public int order() { return 1300; } @Override public void handle(WebContext ctx) throws Throwable { UploadSpec upload = ctx.node().upload(); if (upload == null) { Logs.http().error("{}缺少 @Upload", ctx.rawAct()); throw BizException.create(HttpErrorCode.UPLOAD_ANNOTATION_MISS, I18n.get("sumk.http.upload.error.annocation", "不是上传接口:{0}", ctx.rawAct())); } Collection list = ctx.httpRequest().getParts(); if (list == null || list.isEmpty()) { throw BizException.create(HttpErrorCode.FILE_MISS, "没有文件"); } List files = new ArrayList<>(list.size()); for (Part p : list) { if (upload.paramName().equals(p.getName())) { ctx.data(InnerHttpUtil.extractData(p.getInputStream(), (int) p.getSize())); continue; } files.add(new MultiItemImpl(p)); } MultipartHolder.set(Collections.unmodifiableList(files)); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/MultipartHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import java.util.List; public final class MultipartHolder { private static final ThreadLocal> items = new ThreadLocal<>(); public static List get() { return items.get(); } static void set(List fs) { items.set(fs); } public static void remove() { items.remove(); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/MultipartItem.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import java.io.IOException; import java.io.InputStream; import java.util.Collection; public interface MultipartItem { String getName(); long getSize(); InputStream getInputStream() throws IOException; String getSubmittedFileName(); String getHeader(String name); Collection getHeaders(String name); Collection getHeaderNames(); String getContentType(); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/ReqDataHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import javax.servlet.http.HttpServletRequest; import org.yx.annotation.Bean; import org.yx.http.kit.InnerHttpUtil; import org.yx.log.Logs; @Bean public class ReqDataHandler implements HttpHandler { @Override public int order() { return 1300; } @Override public boolean supportRestType(String type) { return RestType.TEXT.equals(type); } @Override public void handle(WebContext ctx) throws Exception { if (ctx.data() != null) { Logs.http().debug("data is not null"); return; } if (ctx.node().paramLength() == 0) { return; } HttpServletRequest req = ctx.httpRequest(); String data = req.getParameter("data"); if (data != null) { ctx.data(data); return; } ctx.data(InnerHttpUtil.extractData(req.getInputStream(), req.getContentLength())); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/ReqSignValidateHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.conf.AppInfo; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.WebUtil; import org.yx.http.kit.HttpCiphers; import org.yx.log.Logs; import org.yx.util.StringUtil; @Bean public class ReqSignValidateHandler implements HttpHandler { private byte[] salt; @Override public int order() { return 1600; } public ReqSignValidateHandler() { String saltStr = AppInfo.get("sumk.http.sign.salt"); if (StringUtil.isNotEmpty(saltStr)) { salt = saltStr.getBytes(); } } @Override public void handle(WebContext ctx) throws Exception { if (!ctx.node().sign()) { return; } byte[] bs = ctx.getDataInByteArray(); if (bs == null) { return; } String sign = WebUtil.fromHeaderOrParamter(ctx.httpRequest(), "__sign"); if (StringUtil.isEmpty(sign)) { throw BizException.create(HttpErrorCode.SIGN_EMPTY, "签名不能为空"); } if (salt != null) { byte[] temp = new byte[bs.length + salt.length]; System.arraycopy(bs, 0, temp, 0, bs.length); System.arraycopy(salt, 0, temp, bs.length, salt.length); bs = temp; } String sign1 = HttpCiphers.getSigner().sign(bs, ctx.httpRequest()); if (!sign.equals(sign1)) { Logs.http().debug("client sign:{},computed is:{}", sign, sign1); throw BizException.create(HttpErrorCode.SIGN_MISTAKE, "签名验证错误"); } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/ReqToStringHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; @Bean public class ReqToStringHandler implements HttpHandler { @Override public int order() { return 1700; } @Override public void handle(WebContext ctx) throws Exception { Object obj = ctx.data(); if (obj == null) { return; } if (!(obj instanceof byte[])) { return; } byte[] bs = (byte[]) obj; String data = new String(bs, ctx.charset()); ctx.data(data); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/ReqUserHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.base.context.ActionContext; import org.yx.conf.AppInfo; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.WebUtil; import org.yx.http.kit.HttpSettings; import org.yx.http.user.SessionObject; import org.yx.http.user.UserSession; import org.yx.http.user.WebSessions; import org.yx.log.Logs; import org.yx.util.StringUtil; @Bean public class ReqUserHandler implements HttpHandler { private boolean tokenMode = AppInfo.getBoolean("sumk.http.session.token", false); @Override public int order() { return 1200; } @Override public void handle(WebContext ctx) throws Exception { if (ctx.node().requireLogin()) { checkSession(WebUtil.getSessionId(), WebUtil.getUserFlag(ctx.httpRequest())); } } public void checkSession(String sessionId, String userId) { if (!WebSessions.getSessionIdVerifier().test(sessionId)) { Logs.http().warn("sessionId:{}, is not valid", sessionId); throw BizException.create(HttpErrorCode.SESSION_ERROR, "token无效"); } UserSession session = WebSessions.loadUserSession(); SessionObject obj = tokenMode ? session.getUserObject(sessionId, SessionObject.class) : session.loadAndRefresh(sessionId, SessionObject.class); if (obj == null) { if (HttpSettings.isSingleLogin() && StringUtil.isNotEmpty(userId) && session.sessionId(userId) != null) { Logs.http().info("sessionId:{}, login by other place", sessionId); throw BizException.create(HttpErrorCode.LOGIN_AGAIN, "您已在其他地方登录!"); } Logs.http().info("sessionId:{}, 没找到对应的session", sessionId); throw BizException.create(HttpErrorCode.SESSION_ERROR, "请重新登录"); } ActionContext.current().userId(obj.getUserId()); Long deadTime = obj.getExpiredTime(); if (deadTime != null) { if (deadTime < System.currentTimeMillis()) { Logs.http().warn("sessionId:{}, expiredTime:{},使用时间太长", sessionId, deadTime); throw BizException.create(HttpErrorCode.SESSION_ERROR, "session使用时间太长"); } } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/RespBodyHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; @Bean public class RespBodyHandler implements HttpHandler { @Override public int order() { return 2600; } @Override public void handle(WebContext ctx) throws Throwable { byte[] data = (byte[]) ctx.result(); if (data != null && data.length > 0) { ctx.httpResponse().getOutputStream().write(data); } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/RespToStringHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; import org.yx.http.HttpJson; @Bean public class RespToStringHandler implements HttpHandler { @Override public int order() { return 2100; } @Override public void handle(WebContext ctx) throws Throwable { Object obj = ctx.result(); if (obj == null) { if (ctx.node().getReturnType() == void.class) { ctx.result(new byte[0], false); return; } ctx.result(HttpJson.operator().toJson(obj), true); return; } Class clz = obj.getClass(); if (clz == byte[].class) { return; } if (clz.isPrimitive() || clz.equals(String.class)) { ctx.result(String.valueOf(obj), true); return; } ctx.result(HttpJson.operator().toJson(obj), true); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/RestType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; public final class RestType { /** * 普通的http请求 */ public static final String TEXT = "text"; /** * 文件上传 */ public static final String MULTI_PART = "multipart"; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/ToBytesHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import org.yx.annotation.Bean; @Bean public class ToBytesHandler implements HttpHandler { @Override public int order() { return 2200; } @Override public void handle(WebContext ctx) throws Exception { Object result = ctx.result(); if (result == null || result.getClass() == byte[].class) { return; } String bs = (String) result; ctx.result(bs.getBytes(ctx.charset()), false); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/handler/WebContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.handler; import java.nio.charset.Charset; import java.util.List; import java.util.Objects; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.bean.aop.context.NodeContext; import org.yx.http.act.HttpActionInfo; import org.yx.http.act.HttpActionNode; public class WebContext extends NodeContext { private final HttpActionInfo actionInfo; private final HttpServletRequest httpRequest; private final HttpServletResponse httpResponse; private final Charset charset; private Object data; private int lowestOrder; private Object result; private transient String str_data; private transient String str_resp; private boolean failed; public String dataInString() { return str_data; } public String respInString() { return str_resp; } public Object result() { return result; } void result(Object result, boolean cacheInStr) { this.result = result; if (cacheInStr && result != null && String.class == result.getClass()) { this.str_resp = (String) result; } } public WebContext(HttpActionInfo info, HttpServletRequest req, HttpServletResponse resp, Charset charset) { this.actionInfo = info; this.httpRequest = Objects.requireNonNull(req); this.charset = Objects.requireNonNull(charset); this.httpResponse = resp; } public Charset charset() { return this.charset; } public Object data() { return data; } public byte[] getDataInByteArray() { if (data instanceof String) { return ((String) data).getBytes(charset()); } return (byte[]) data; } public HttpServletRequest httpRequest() { return httpRequest; } public HttpServletResponse httpResponse() { return httpResponse; } void data(Object data) { this.data = data; if (data != null && String.class == data.getClass()) { this.str_data = (String) data; } } /** * 开发者定义的原始act,这里的act不包含逗号。 如果有逗号分隔,这里就只是@Web中的一段 * * @return 就是@Web上定义的act,不为null */ public String rawAct() { return this.actionInfo.rawAct(); } public int getLowestOrder() { return lowestOrder; } public void setLowestOrder(int lowestOrder) { this.lowestOrder = lowestOrder; } public List tags() { return this.node().tags(); } public boolean isFailed() { return failed; } public void setFailed(boolean failed) { this.failed = failed; } public HttpActionInfo actionInfo() { return actionInfo; } @Override public HttpActionNode node() { return this.actionInfo.node(); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/invoke/WebHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.invoke; import java.util.List; import org.yx.bean.IOC; import org.yx.http.WebFilter; import org.yx.http.handler.WebContext; public final class WebHandler { private static WebFilter filter; private static final WebFilter LAST = new WebFilter() { private final WebVisitor visitor = new WebVisitorImpl(); @Override public Object doFilter(WebContext ctx) throws Throwable { return visitor.visit(ctx); } }; public static synchronized void init() { if (filter != null) { return; } List list = IOC.getBeans(WebFilter.class); if (list == null || list.isEmpty()) { filter = LAST; return; } final int size = list.size(); for (int i = 0; i < size; i++) { WebFilter current = list.get(i); if (i == size - 1) { current.setNext(LAST); break; } current.setNext(list.get(i + 1)); } filter = list.get(0); } public static Object handle(WebContext ctx) throws Throwable { ctx.setParamPojo(ctx.node().buildParamPojo(ctx.data())); return filter.doFilter(ctx); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/invoke/WebVisitor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.invoke; import org.yx.http.handler.WebContext; public interface WebVisitor { Object visit(WebContext ctx) throws Throwable; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/invoke/WebVisitorImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.invoke; import org.yx.http.act.HttpActionNode; import org.yx.http.handler.WebContext; public class WebVisitorImpl implements WebVisitor { @Override public Object visit(WebContext ctx) throws Throwable { HttpActionNode http = ctx.node(); return http.execute(ctx.getParamPojo()); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/DefaultHttpEncryptor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import java.util.Objects; import org.yx.common.util.S; import org.yx.common.util.secury.Encryptor; import org.yx.exception.BizException; import org.yx.http.HttpEncryptor; import org.yx.http.HttpErrorCode; import org.yx.http.WebUtil; import org.yx.http.handler.WebContext; public class DefaultHttpEncryptor implements HttpEncryptor { private Encryptor cipher = S.cipher(); protected byte[] getKey() { byte[] key = WebUtil.getSessionEncryptKey(); if (key == null) { throw new BizException(HttpErrorCode.SESSION_KEY_NOT_FOUND, "加密用的key没有找到"); } return key; } @Override public byte[] encrypt(byte[] data, WebContext ctx) throws Exception { return cipher.encrypt(data, getKey()); } @Override public byte[] decrypt(byte[] data, WebContext ctx) throws Exception { return cipher.decrypt(data, getKey()); } public Encryptor getCipher() { return cipher; } public void setCipher(Encryptor cipher) { this.cipher = Objects.requireNonNull(cipher); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/DefaultHttpKit.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.common.locale.I18n; import org.yx.conf.AppInfo; import org.yx.http.HttpJson; import org.yx.log.Logs; import org.yx.util.StringUtil; public class DefaultHttpKit implements HttpKit { private static final int MAX_EXPECT_SIZE = 1024 * 1024; private static final int MIN_EXPECT_SIZE = 64; @Override public Charset charset(HttpServletRequest req) { String charsetName = req.getCharacterEncoding(); if (StringUtil.isEmpty(charsetName)) { return HttpSettings.defaultCharset(); } if ("UTF8".equalsIgnoreCase(charsetName) || "UTF-8".equalsIgnoreCase(charsetName)) { return StandardCharsets.UTF_8; } if (!Charset.isSupported(charsetName)) { Logs.http().warn("charset '{}' is not supported,use default charset {}", charsetName, HttpSettings.defaultCharset()); return HttpSettings.defaultCharset(); } return Charset.forName(charsetName); } @Override public void sendError(HttpServletResponse resp, String code, String errorMsg, Charset charset) throws IOException { resp.setStatus(HttpSettings.errorHttpStatus()); Map map = new LinkedHashMap<>(2, 1); if (AppInfo.getBoolean("sumk.http.interrorcode", false)) { int c = 0; try { c = Integer.valueOf(code); } catch (Exception e) { Logs.http().error(code + "不能转为int"); } map.put("code", c); } else { map.put("code", code); } String i18nMsg = I18n.get("sumk.http.error." + code, errorMsg); map.put("message", i18nMsg); resp.getOutputStream().write(HttpJson.operator().toJson(map).getBytes(charset)); } @Override public void setRespHeader(HttpServletResponse resp, Charset charset) throws IOException { resp.setContentType("application/json;charset=" + charset.name()); } @Override public int expectReqDataSize(int expect) { if (expect < 0) { return 1024; } if (expect < MIN_EXPECT_SIZE) { return MIN_EXPECT_SIZE; } if (expect > MAX_EXPECT_SIZE) { return MAX_EXPECT_SIZE; } return expect; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/HttpCiphers.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import java.util.Objects; import org.yx.common.util.S; import org.yx.http.HttpEncryptor; import org.yx.http.Signer; public final class HttpCiphers { private static HttpEncryptor encryptor = new DefaultHttpEncryptor(); public static HttpEncryptor getEncryptor() { return encryptor; } public static void setEncryptor(HttpEncryptor encryptor) { HttpCiphers.encryptor = Objects.requireNonNull(encryptor); } private static Signer signer = (bs, httpServletRequest) -> S.hash().digestByteToString(bs); public static Signer getSigner() { return signer; } public static void setSigner(Signer signer) { HttpCiphers.signer = Objects.requireNonNull(signer); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/HttpKit.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import java.io.IOException; import java.nio.charset.Charset; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface HttpKit { Charset charset(HttpServletRequest req); void sendError(HttpServletResponse resp, String code, String errorMsg, Charset charset) throws IOException; void setRespHeader(HttpServletResponse resp, Charset charset) throws IOException; int expectReqDataSize(int expect); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/HttpSettings.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import static org.yx.http.server.HttpMethod.DELETE; import static org.yx.http.server.HttpMethod.GET; import static org.yx.http.server.HttpMethod.PATCH; import static org.yx.http.server.HttpMethod.POST; import static org.yx.http.server.HttpMethod.PUT; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public final class HttpSettings { private static final String DEFAULT_TEST_KEY = "thisIsTest"; private static int errorHttpStatus; private static long httpSessionTimeoutInMs; private static boolean cookieEnable; private static int maxReqLogSize; private static int maxRespLogSize; private static int warnTime; private static int infoTime; private static Charset defaultCharset = StandardCharsets.UTF_8; private static int maxHttpBody; private static String plainKey; private static boolean singleLogin; private static String traceHeaderName; private static String testKey = DEFAULT_TEST_KEY; private static Map headers; private static List defaultHttpMethods = CollectionUtil.unmodifyList(new String[] { POST, GET }); private static final List ALL_HTTP_METHODS = CollectionUtil .unmodifyList(new String[] { POST, GET, DELETE, PUT, PATCH }); private static List allHttpMethods = ALL_HTTP_METHODS; public static List allHttpMethods() { return allHttpMethods; } public static List defaultHttpMethods() { return defaultHttpMethods; } public static String testKey() { return testKey; } public static int errorHttpStatus() { return errorHttpStatus; } public static int maxHttpBody() { return maxHttpBody; } public static long httpSessionTimeoutInMs() { return httpSessionTimeoutInMs; } public static boolean isCookieEnable() { return cookieEnable; } public static boolean isUploadEnable() { return AppInfo.getBoolean("sumk.http.upload.enable", true); } public static int maxReqLogSize() { return maxReqLogSize; } public static int maxRespLogSize() { return maxRespLogSize; } public static int warnTime() { return warnTime; } public static int infoTime() { return infoTime; } public static boolean isSingleLogin() { return singleLogin; } public static boolean allowPlain(HttpServletRequest request) { String plainKey = HttpSettings.plainKey; return plainKey != null && plainKey.equals(request.getParameter("_plainKey")); } public static Map responseHeaders() { return headers; } static List splitAndTrim(String v) { List list = StringUtil.splitAndTrim(v, ",", ";"); return CollectionUtil.unmodifyList(list.toArray(new String[list.size()])); } public static void init() { HttpSettings.errorHttpStatus = AppInfo.getInt("sumk.http.errorcode", 550); String c = AppInfo.get("sumk.http.charset"); if (StringUtil.isNotEmpty(c)) { try { HttpSettings.defaultCharset = Charset.forName(c); } catch (Exception e) { Logs.http().error("{}不是有效的字符集编码", c); } } String methods = AppInfo.getLatin("sumk.http.method.default", null); if (methods != null) { defaultHttpMethods = splitAndTrim(methods); } methods = null; AppInfo.addObserver(info -> { HttpSettings.maxReqLogSize = AppInfo.getInt("sumk.http.log.reqsize", 1000); HttpSettings.maxRespLogSize = AppInfo.getInt("sumk.http.log.respsize", 5000); HttpSettings.warnTime = AppInfo.getInt("sumk.http.log.warn.time", 3000); HttpSettings.infoTime = AppInfo.getInt("sumk.http.log.info.time", 1000); HttpSettings.maxHttpBody = AppInfo.getInt("sumk.http.body.maxLength", 1024 * 1024 * 100); HttpSettings.singleLogin = AppInfo.getBoolean("sumk.http.session.single", false); String plain = AppInfo.get("sumk.http.plain.key", null); HttpSettings.plainKey = "".equals(plain) ? null : plain; HttpSettings.cookieEnable = AppInfo.getBoolean("sumk.http.header.usecookie", true); HttpSettings.httpSessionTimeoutInMs = 1000L * AppInfo.getInt("sumk.http.session.timeout", 60 * 30); HttpSettings.traceHeaderName = AppInfo.get("sumk.http.header.trace", "s-trace"); Map map = CollectionUtil.unmodifyMap(AppInfo.subMap("s.http.response.header.")); HttpSettings.headers = map.isEmpty() ? null : map; HttpSettings.testKey = AppInfo.get("sumk.http.testkey", DEFAULT_TEST_KEY); String ms = AppInfo.getLatin("sumk.http.method.all", null); HttpSettings.allHttpMethods = ms == null ? ALL_HTTP_METHODS : splitAndTrim(ms); }); } public static Charset defaultCharset() { return defaultCharset; } public static String traceHeaderName() { return traceHeaderName; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/InnerHttpUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.function.BiConsumer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.base.context.ActionContext; import org.yx.base.context.AppContext; import org.yx.base.sumk.UnsafeByteArrayOutputStream; import org.yx.common.action.ActionStatis; import org.yx.common.action.ActionStatisImpl; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.MessageType; import org.yx.http.WebUtil; import org.yx.log.Logs; import org.yx.util.UUIDSeed; public final class InnerHttpUtil { private static HttpKit kit = new DefaultHttpKit(); private static ActionStatis actStatis = new ActionStatisImpl(); private static BiConsumer optionMethodHandler; public static BiConsumer getOptionMethodHandler() { return optionMethodHandler; } public static void setOptionMethodHandler(BiConsumer optionMethodHandler) { InnerHttpUtil.optionMethodHandler = optionMethodHandler; } public static void setActionStatis(ActionStatis actStatis) { InnerHttpUtil.actStatis = Objects.requireNonNull(actStatis); } public static HttpKit getKit() { return kit; } public static void setKit(HttpKit kit) { InnerHttpUtil.kit = Objects.requireNonNull(kit); } public static byte[] extractData(InputStream in, int expectSize) throws IOException { int count = 0; int n = 0; expectSize = kit.expectReqDataSize(expectSize); if (Logs.http().isTraceEnabled()) { Logs.http().trace("expect request content length: {}", expectSize); } byte[] temp = new byte[512]; @SuppressWarnings("resource") UnsafeByteArrayOutputStream output = new UnsafeByteArrayOutputStream(expectSize); while (-1 != (n = in.read(temp))) { output.write(temp, 0, n); count += n; if (count > HttpSettings.maxHttpBody()) { throw BizException.create(HttpErrorCode.BODY_TOO_BIG, "请求数据太长"); } } byte[] bs = output.extractHttpBodyData(); output.close(); return bs; } public static Charset charset(HttpServletRequest req) { return kit.charset(req); } public static void record(String act, long time, boolean isSuccess) { actStatis.visit(act, time, isSuccess); } public static ActionStatis getActionStatis() { return actStatis; } public static void sendError(HttpServletResponse resp, int code, String errorMsg, Charset charset) { sendError(resp, String.valueOf(code), errorMsg, charset); } public static void sendError(HttpServletResponse resp, String code, String message, Charset charset) { try { kit.sendError(resp, code, message, charset); } catch (IOException e) { Logs.http().error(e.getLocalizedMessage(), e); } } public static void setRespHeader(HttpServletResponse resp, Charset charset) throws IOException { kit.setRespHeader(resp, charset); } public static boolean preServerHandle(HttpServletRequest req, HttpServletResponse resp, String firstKey) { resp.setContentType("text/plain;charset=UTF-8"); String md5 = AppInfo.get(firstKey, "sumk.union.monitor", "b914914b9850d74435056a37cdd915f2b0fb7e23401034cf8579676ff2cce7db"); String sign = WebUtil.fromHeaderOrParamter(req, "sign"); if (sign == null) { Logs.http().debug("sign is empty"); return false; } try { String signed = S.hash().digest(sign, StandardCharsets.UTF_8); if (!md5.equalsIgnoreCase(signed)) { Logs.http().debug("signed:{},need:{}", signed, md5); return false; } } catch (Exception e) { } return true; } public static void startContext(HttpServletRequest req, HttpServletResponse resp, String act) { String traceId = UUIDSeed.seq18(); String pre = req.getHeader("trace-action"); if (pre != null && pre.length() > 0 && AppInfo.getBoolean("sumk.http.traceid.pre.allow", true)) { traceId = String.join(".", pre, traceId); } ActionContext.newContext(act, traceId, AppContext.inst().isTest() && "1".equals(req.getParameter(HttpSettings.testKey()))); resp.setHeader(HttpSettings.traceHeaderName(), traceId); } public static MessageType parseMessageType(String name) { if (name == null || name.isEmpty()) { return MessageType.PLAIN; } name = name.toUpperCase(); if (name.equals("ENCRYPT_BASE64") || name.equals("ENCRYPTBASE64")) { return MessageType.ENCRYPT_BASE64; } if (name.equals("BASE64")) { return MessageType.BASE64; } if (name.equals("ENCRYPT")) { return MessageType.ENCRYPT; } Logs.http().warn("配置值{}对应的MessageType是PLAIN", name); return MessageType.PLAIN; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/kit/LocalWebContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.kit; import org.yx.http.handler.WebContext; public final class LocalWebContext { private static final ThreadLocal CTX = new ThreadLocal<>(); public static WebContext getCtx() { return CTX.get(); } public static void setCtx(WebContext ctx) { CTX.set(ctx); } public static void remove() { CTX.remove(); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/log/HttpLogHandler.java ================================================ package org.yx.http.log; import javax.servlet.http.HttpServletRequest; import org.yx.http.handler.WebContext; /** * 这个在用户线程里执行,可以取到线程变量。处理最好用异步,要不然会阻塞请求,影响用户体验 */ public interface HttpLogHandler { void log(WebContext ctx, HttpServletRequest req, Throwable ex, long time); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/log/HttpLogs.java ================================================ package org.yx.http.log; import java.nio.charset.Charset; import java.util.Objects; import javax.servlet.http.HttpServletRequest; import org.yx.http.HttpJson; import org.yx.http.handler.WebContext; import org.yx.log.LogKits; public class HttpLogs { private static HttpLogHandler handler = new PlainHttpLogHandler(); public static HttpLogHandler getHandler() { return handler; } public static void setHandler(HttpLogHandler handler) { HttpLogs.handler = Objects.requireNonNull(handler); } public static void log(WebContext ctx, HttpServletRequest req, Throwable ex, long time) { handler.log(ctx, req, ex, time); } public static String getParam(WebContext wc, int maxLength) { if (wc == null) { return null; } return parse(wc.dataInString(), wc.data(), maxLength, wc.charset()); } public static String getResponse(WebContext wc, int maxLength) { if (wc == null) { return null; } return parse(wc.respInString(), wc.result(), maxLength, wc.charset()); } public static String parse(String data, Object obj, int maxLength, Charset charset) { if (data != null) { return LogKits.shorterSubfix(data, maxLength); } if (obj == null) { return null; } if (obj.getClass() == byte[].class) { byte[] bs = (byte[]) obj; int len = bs.length; if (len > maxLength * 3) { len = maxLength * 3; } String temp = new String(bs, 0, len, charset); return LogKits.shorterSubfix(temp, maxLength); } String resp = HttpJson.operator().toJson(obj); return LogKits.shorterSubfix(resp, maxLength); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/log/PlainHttpLogHandler.java ================================================ package org.yx.http.log; import static org.yx.conf.AppInfo.LN; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.yx.exception.BizException; import org.yx.http.act.HttpActions; import org.yx.http.handler.WebContext; import org.yx.http.kit.HttpSettings; import org.yx.log.Log; import org.yx.util.StringUtil; public class PlainHttpLogHandler implements HttpLogHandler { protected Logger log = Log.get("sumk.http.log"); @Override public void log(WebContext ctx, HttpServletRequest req, Throwable ex, final long time) { if (ex != null && log.isErrorEnabled()) { if (ctx != null) { logError(ctx, ex, time); } else { logError(req, ex, time); } return; } if (time >= HttpSettings.warnTime() && log.isWarnEnabled()) { log.warn(buildLogMsg(ctx, req, time)); return; } if (time >= HttpSettings.infoTime() && log.isInfoEnabled()) { log.info(buildLogMsg(ctx, req, time)); return; } if (log.isDebugEnabled()) { log.debug(buildLogMsg(ctx, req, time)); return; } } protected void logError(HttpServletRequest req, Throwable ex, long time) { StringBuilder sb = newStringBuilder(req); sb.append(req.getRequestURI()).append(" remote:").append(remoteAddr(req)).append(" time:").append(time); logError(sb.toString(), ex); } protected void logError(WebContext wc, Throwable ex, long time) { StringBuilder sb = newStringBuilder(wc.httpRequest()); sb.append(getRawAct(wc)).append(" remote:").append(remoteAddr(wc.httpRequest())).append(" time:") .append(time).append(LN).append(" param: ").append(getParam(wc, HttpSettings.maxReqLogSize())); logError(sb.toString(), ex); } protected void logError(String msg, Throwable ex) { if (ex instanceof BizException) { log.warn(msg, ex); return; } log.error(msg, ex); } protected StringBuilder newStringBuilder(HttpServletRequest req) { return new StringBuilder(64).append("[").append(req.getMethod()).append("] "); } protected String buildLogMsg(WebContext wc, HttpServletRequest req, long time) { StringBuilder sb = newStringBuilder(req); if (wc != null) { sb.append(getRawAct(wc)).append(" remote:").append(remoteAddr(req)).append(" time:").append(time) .append(LN).append(" param: ").append(getParam(wc, HttpSettings.maxReqLogSize())).append(LN) .append(" resp: ").append(getResponse(wc, HttpSettings.maxRespLogSize())); return sb.toString(); } sb.append(req.getRequestURI()).append(" time:").append(time); return sb.toString(); } protected String remoteAddr(HttpServletRequest req) { String ip = req.getHeader("X-Real-IP"); return StringUtil.isNotEmpty(ip) ? ip : req.getRemoteAddr(); } protected String getParam(WebContext wc, int maxLength) { return HttpLogs.getParam(wc, maxLength); } protected String getResponse(WebContext wc, int maxLength) { return HttpLogs.getResponse(wc, maxLength); } protected String getRawAct(WebContext wc) { String act = wc.rawAct(); return act.endsWith(HttpActions.PREFIX_MATCH_ENDING) ? wc.httpRequest().getPathInfo() : act; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/monitor/HttpMonitors.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.monitor; import static org.yx.common.monitor.Monitors.BLANK; import static org.yx.conf.AppInfo.LN; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.bean.InnerIOC; import org.yx.bean.NameSlot; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.log.LogLevel; import org.yx.log.Loggers; import org.yx.main.SumkServer; import org.yx.util.SumkDate; public final class HttpMonitors { public static String serverInfo() { long startTime = SumkServer.startTime(); long now = System.currentTimeMillis(); long ms = now - startTime; StringBuilder sb = new StringBuilder(); sb.append("start").append(BLANK).append(SumkDate.of(startTime).to_yyyy_MM_dd_HH_mm_ss_SSS().replace(" ", "T")) .append(BLANK).append("run(ms)").append(BLANK).append(ms).append(LN).append("localip").append(BLANK) .append(AppInfo.getLocalIp()).append(BLANK).append("pid").append(BLANK).append(AppInfo.pid()).append(LN) .append("framework").append(BLANK).append(Const.sumkVersion()); String v = AppInfo.appId(null); if (v != null) { sb.append(BLANK).append("appId").append(BLANK).append(v); } return sb.toString(); } public static String systemInfo() { Map map = new HashMap<>(System.getProperties()); StringBuilder sb = new StringBuilder(); map.forEach((k, v) -> { if (v != null) { v = v.toString().replace("\r", "\\r").replace("\n", "\\n"); } sb.append(k).append(" : ").append(v).append(LN); }); return sb.toString(); } public static String jvmInfo() { StringBuilder sb = new StringBuilder(); sb.append("## name init max commited used").append(LN); DecimalFormat f = new DecimalFormat("#,###"); List mpmxbs = ManagementFactory.getMemoryPoolMXBeans(); for (MemoryPoolMXBean mpmxb : mpmxbs) { if (mpmxb == null || mpmxb.getUsage() == null) { continue; } String name = mpmxb.getName(); if (name == null || name.isEmpty()) { continue; } sb.append(name).append(BLANK).append(f.format(mpmxb.getUsage().getInit())).append(BLANK) .append(f.format(mpmxb.getUsage().getMax())).append(BLANK) .append(f.format(mpmxb.getUsage().getCommitted())).append(BLANK) .append(f.format(mpmxb.getUsage().getUsed())).append(LN); } return sb.toString(); } public static String stack(boolean full) { Predicate ignore = full ? BooleanMatcher.FALSE : Matchers.createWildcardMatcher( "org.yx.*,java.*,javax.*,sun.*,org.eclipse.jetty.*,org.apache.zookeeper.*,io.netty.*,org.apache.mina.*"); Map map = Thread.getAllStackTraces(); StringBuilder all = new StringBuilder(2000).append("线程总数:").append(map.size()).append(LN); for (Entry en : map.entrySet()) { Thread t = en.getKey(); StackTraceElement[] st = en.getValue(); boolean matched = false; StringBuilder sb = new StringBuilder(256).append(t.getName()).append(" [id:").append(t.getId()).append("]") .append(BLANK).append(t.getState()).append(LN); boolean first = true; for (StackTraceElement e : st) { if (first) { first = false; } else { sb.append(BLANK); } if (!ignore.test(e.getClassName())) { matched = true; } sb.append(e.getClassName()).append('.').append(e.getMethodName()); if (e.getLineNumber() > 0) { sb.append(" ").append(e.getLineNumber()); } sb.append(LN); } if (matched) { all.append(sb.append(LN)); } } return all.toString(); } public static String threadPoolInfo(ThreadPoolExecutor pool) { StringBuilder sb = new StringBuilder(); sb.append("active").append(BLANK).append(pool.getActiveCount()).append(BLANK).append("size").append(BLANK) .append(pool.getPoolSize()).append(BLANK).append("queue").append(BLANK).append(pool.getQueue().size()) .append(BLANK).append(LN).append("max").append(BLANK).append(pool.getMaximumPoolSize()).append(BLANK) .append("keepAlive(ms)").append(BLANK).append(pool.getKeepAliveTime(TimeUnit.MILLISECONDS)) .append(BLANK) .append("completed*").append(BLANK).append(pool.getCompletedTaskCount()); return sb.toString(); } public static List beans() { Collection beans = InnerIOC.beans(); List names = new ArrayList<>(); for (Object obj : beans) { names.add(obj.getClass().getName()); } Collections.sort(names); return names; } public static String beansName() { StringBuilder sb = new StringBuilder(); List names = InnerIOC.beanNames(); Collections.sort(names); for (String name : names) { NameSlot slot = InnerIOC.getSlot(name); sb.append(slot).append(Const.LN); } return sb.toString(); } public static String logLevels() { Map map = new TreeMap<>(Loggers.currentLevels()); StringBuilder sb = new StringBuilder("#logLevels:").append(LN); char[] black = new char[7]; Arrays.fill(black, ' '); map.forEach((k, v) -> { sb.append(v).append(black, 0, black.length - v.name().length()).append(k).append(LN); }); return sb.toString(); } public static String sumkDateCacheChangeCount() { StringBuilder sb = new StringBuilder(); sb.append("##sumkDateCached").append(BLANK).append(SumkDate.cacheChangeCount()); return sb.toString(); } public static String gcInfo() { StringBuilder sb = new StringBuilder(64).append("##name").append(BLANK).append("count").append(BLANK) .append("time(ms)"); for (GarbageCollectorMXBean mxBean : ManagementFactory.getGarbageCollectorMXBeans()) { sb.append(LN).append(getGcName(mxBean.getName())).append(BLANK).append(mxBean.getCollectionCount()) .append(BLANK).append(mxBean.getCollectionTime()); } return sb.toString(); } private static String getGcName(String name) { if ("PS Scavenge".equals(name) || "ParNew".equals(name) || "G1 Young Generation".equals(name) || "Copy".equals(name)) { return "Young"; } if ("PS MarkSweep".equals(name) || "ConcurrentMarkSweep".equals(name) || "G1 Old Generation".equals(name) || "MarkSweepCompact".equals(name)) { return "Old"; } return name; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/select/DefaultHttpActionSelector.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.select; import static org.yx.base.matcher.Matchers.WILDCARD; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Function; import org.yx.common.StringEntity; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.http.act.HttpActionInfo; import org.yx.http.act.HttpActionNode; import org.yx.http.act.HttpActions; import org.yx.http.act.IgnoreNameActionInfo; import org.yx.http.act.PrefixMappingActionInfo; import org.yx.log.Logs; public class DefaultHttpActionSelector implements HttpActionSelector { private Map actMap = Collections.emptyMap(); private HttpActionInfo[] starts; private HttpActionInfo[] lastMappings; @Override public HttpActionInfo getHttpInfo(String act, String method) { method = method.toUpperCase(); HttpActionInfo info = this.getAcceptedAction(actMap.get(act), act, method); if (info != null) { return info; } info = this.getAcceptedAction(this.starts, act, method); if (info != null) { return info; } return this.getAcceptedAction(this.lastMappings, act, method); } protected HttpActionInfo getAcceptedAction(HttpActionInfo[] infos, String act, String method) { if (infos == null) { return null; } for (HttpActionInfo info : infos) { if (info.match(act, method)) { return info; } } return null; } @Override public Collection actions() { List list = new ArrayList<>(200); for (HttpActionInfo[] infos : this.actMap.values()) { for (HttpActionInfo info : infos) { list.add(info); } } list.sort(null); if (this.starts != null) { list.addAll(Arrays.asList(this.starts)); } if (this.lastMappings != null) { list.addAll(Arrays.asList(this.lastMappings)); } return list; } protected boolean isDuplicate(HttpActionInfo info, HttpActionInfo[] old) { for (String m : info.node().methods()) { for (HttpActionInfo o : old) { if (o.node().acceptMethod(m)) { return true; } } } return false; } protected void appendStart(PrefixMappingActionInfo info, List startList) { for (PrefixMappingActionInfo old : startList) { if (!Objects.equals(info.getUrlStart(), old.getUrlStart())) { continue; } this.urlDuplicate(info.formalName()); for (String m : info.node().methods()) { if (old.node().acceptMethod(m)) { throw new SumkException(345647432, "web接口" + old.formalName() + "重复了,http方法是" + m); } } } startList.add(info); } protected void urlDuplicate(String url) { String key = "sumk.http.url.duplicate"; if (!AppInfo.getBoolean(key, false)) { throw new SumkException(345647431, "web接口" + url + "重复了,设置" + key + "=1可以开启url重名功能"); } } @Override public void init(List> infos, Function nameResolver) { Map actMap = new HashMap<>(infos.size() * 2); List startList = new ArrayList<>(); for (StringEntity kv : infos) { String parsedName = nameResolver.apply(kv.key()); if (parsedName.endsWith(HttpActions.PREFIX_MATCH_ENDING)) { this.appendStart(new PrefixMappingActionInfo(kv.key(), kv.value(), parsedName, parsedName.substring(0, parsedName.length() - 1)), startList); continue; } HttpActionInfo info = new IgnoreNameActionInfo(kv.key(), kv.value(), parsedName); HttpActionInfo[] old = actMap.get(parsedName); if (old == null) { actMap.put(parsedName, new HttpActionInfo[] { info }); continue; } this.urlDuplicate(parsedName); if (this.isDuplicate(info, old)) { throw new SumkException(345647432, "web接口" + parsedName + "重复了"); } HttpActionInfo[] newInfos = Arrays.copyOf(old, old.length + 1); newInfos[newInfos.length - 1] = info; actMap.put(parsedName, newInfos); } if (startList.size() > 0) { String key = "sumk.http.url.match.prefix"; if (!AppInfo.getBoolean(key, false)) { Logs.http().error("{}接口是前缀匹配,但是本系统没有开启前缀匹配功能。前缀匹配接口共有{}个", startList.get(0).rawAct(), startList.size()); throw new SumkException(345647431, "需要设置" + key + "=1才能开启前缀匹配的功能"); } } startList.sort(null); Collections.reverse(startList); this.lastMappings = actMap.remove(WILDCARD); this.actMap = new HashMap<>(actMap); this.starts = startList.isEmpty() ? null : startList.toArray(new PrefixMappingActionInfo[startList.size()]); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/select/HttpActionSelector.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.select; import java.util.Collection; import java.util.List; import java.util.function.Function; import org.yx.common.StringEntity; import org.yx.http.act.HttpActionInfo; import org.yx.http.act.HttpActionNode; public interface HttpActionSelector { /** * 用于初始化,在sumk启动的时候调用一次。 * * @param infos 里面的key是解析后的act,跟@Web中定义的格式可能不同 * @param nameResolver 名称解析器 */ void init(List> infos, Function nameResolver); /** * 根据真正的act获取HttpActionInfo。对于url含参数等场景,可以返回HttpActionInfo子类 * * @param act 解析后的act * @param method http请求的方法,比如GET、POST,参加HttpMethod接口 * @return 如果没找到,可以返回默认servlet,也可以返回null */ HttpActionInfo getHttpInfo(String act, String method); /** * 里面的接口是不重复的,但可能存在name一样,method不一样的情况。 为了便于查看,建议对里面的顺序做排序 * * @return 返回所有的接口列表,逗号隔开的会被当成多个。 */ Collection actions(); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/AbstractActionServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.yx.base.context.ActionContext; import org.yx.common.locale.I18n; import org.yx.common.validate.InvalidParamException; import org.yx.conf.AppInfo; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.act.HttpActionInfo; import org.yx.http.act.HttpActions; import org.yx.http.handler.WebContext; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.kit.LocalWebContext; import org.yx.http.log.HttpLogs; import org.yx.log.Logs; import org.yx.util.StringUtil; public abstract class AbstractActionServer extends AbstractCommonHttpServlet { private static final long serialVersionUID = 74378082364534491L; protected final Logger log = Logs.http(); @Override protected void handle(HttpServletRequest req, HttpServletResponse resp) { final long beginTime = System.currentTimeMillis(); Throwable ex = null; WebContext wc = null; try { final Charset charset = InnerHttpUtil.charset(req); this.setRespHeader(req, resp, charset); String rawAct = getRawAct(req); if (log.isTraceEnabled()) { log.trace("raw act={}", rawAct); } if (rawAct == null || rawAct.isEmpty()) { log.error("raw act is empty in {}?{}", req.getPathInfo(), req.getQueryString()); throw BizException.create(HttpErrorCode.ACT_FORMAT_ERROR, I18n.get("sumk.http.error.actformat", "{0}请求格式不正确", rawAct)); } HttpActionInfo info = HttpActions.getHttpInfo(rawAct, req.getMethod()); if (info == null) { log.error("{} is not action", rawAct); throw BizException.create(HttpErrorCode.ACT_NOT_FOUND, I18n.get("sumk.http.error.act.notfound", "接口不存在")); } rawAct = info.rawAct(); if (info.node().overflowThreshold()) { throw BizException.create(HttpErrorCode.THREAD_THRESHOLD_OVER, "系统限流降级"); } InnerHttpUtil.startContext(req, resp, rawAct); wc = new WebContext(info, req, resp, charset); LocalWebContext.setCtx(wc); handle(wc); } catch (Throwable e) { try { ex = handleError(req, resp, e); } catch (Exception e2) { ex = e; log.error("处理异常发生错误。可能是网络问题,也可能是异常处理出问题(不该发生)", e2); } } finally { long time = System.currentTimeMillis() - beginTime; HttpLogs.log(wc, req, ex, time); if (wc != null) { InnerHttpUtil.record(wc.rawAct(), time, ex == null && !wc.isFailed()); } LocalWebContext.remove(); ActionContext.remove(); } } protected void sendError(HttpServletRequest req, HttpServletResponse resp, String code, String errorMsg) { InnerHttpUtil.sendError(resp, code, errorMsg, InnerHttpUtil.charset(req)); } protected String getRawAct(HttpServletRequest req) { String act = req.getPathInfo(); if (StringUtil.isEmpty(act) && AppInfo.getBoolean("sumk.http.act.query", false)) { act = req.getParameter("_act"); if (act != null) { act = act.replace('.', '/'); } } return act; } protected void setRespHeader(HttpServletRequest req, HttpServletResponse resp, Charset charset) throws IOException { InnerHttpUtil.setRespHeader(resp, charset); } protected abstract void handle(WebContext wc) throws Throwable; protected Throwable handleError(HttpServletRequest req, HttpServletResponse resp, Throwable e) { Throwable temp = e; if (temp instanceof InvocationTargetException) { temp = ((InvocationTargetException) temp).getTargetException(); } if (temp instanceof InvalidParamException) { sendError(req, resp, String.valueOf(HttpErrorCode.VALIDATE_ERROR), temp.getMessage()); return temp; } Throwable root = temp; do { if (temp instanceof BizException) { BizException be = (BizException) temp; sendError(req, resp, be.getCode(), be.getMessage()); return be; } } while ((temp = temp.getCause()) != null); sendError(req, resp, String.valueOf(HttpErrorCode.HANDLE_ERROR), "请求处理异常"); return root; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/AbstractCommonHttpServlet.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; import java.io.IOException; import java.util.Map.Entry; import java.util.function.BiConsumer; import javax.servlet.GenericServlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.conf.AppInfo; import org.yx.http.HttpErrorCode; import org.yx.http.kit.HttpSettings; import org.yx.http.kit.InnerHttpUtil; public abstract class AbstractCommonHttpServlet extends GenericServlet { private static final long serialVersionUID = 1L; private static final String METHOD_OPTIONS = "OPTIONS"; @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) { throw new ServletException("non-HTTP request or response"); } service((HttpServletRequest) req, (HttpServletResponse) res); } protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (HttpSettings.responseHeaders() != null) { for (Entry entry : HttpSettings.responseHeaders().entrySet()) { resp.setHeader(entry.getKey(), entry.getValue()); } } String method = req.getMethod().toUpperCase(); if (!HttpSettings.allHttpMethods().contains(method)) { if (METHOD_OPTIONS.equals(method)) { this.doOptions(req, resp); return; } InnerHttpUtil.sendError(resp, HttpErrorCode.METHOD_UNSUPPORT, "NOT SUPPORTED", AppInfo.UTF8); return; } this.handle(req, resp); } protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BiConsumer h = InnerHttpUtil.getOptionMethodHandler(); if (h != null) { h.accept(req, resp); return; } StringBuilder sb = new StringBuilder(); for (String m : HttpSettings.allHttpMethods()) { sb.append(m).append(", "); } sb.append(METHOD_OPTIONS); String allow = sb.toString(); resp.setHeader("Allow", allow); } protected abstract void handle(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/DocumentServlet.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.annotation.Bean; import org.yx.annotation.http.SumkServlet; import org.yx.common.json.GsonHelper; import org.yx.common.monitor.Monitors; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.http.act.HttpActions; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.monitor.HttpMonitors; import org.yx.log.Logs; import org.yx.util.StringUtil; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @Bean @SumkServlet(path = { "/_sumk_acts/*" }, loadOnStartup = -1, appKey = "sumkActs") public class DocumentServlet extends AbstractCommonHttpServlet { private static final long serialVersionUID = 1L; @Override protected void handle(HttpServletRequest req, HttpServletResponse resp) throws IOException { if (!InnerHttpUtil.preServerHandle(req, resp, "sumk.acts.info")) { return; } String mode = req.getParameter("mode"); if (StringUtil.isEmpty(mode)) { Logs.http().debug("mode is empty"); return; } GsonBuilder builder = GsonHelper.builder("sumk.acts"); if ("1".equals(req.getParameter("pretty"))) { builder.setPrettyPrinting(); } Gson gson = builder.create(); if (mode.equals("http")) { List> list = HttpActions.infos("1".equals(req.getParameter("full"))); list = this.filter(list, req); write(resp, gson.toJson(list)); return; } if (mode.equals("rpc")) { String key = "1".equals(req.getParameter("full")) ? "rpc-full" : "rpc-simple"; Object ret = Monitors.getMessage("document", key, null); if (ret == null) { return; } @SuppressWarnings("unchecked") List> list = (List>) ret; list = this.filter(list, req); write(resp, gson.toJson(list)); return; } if (mode.equals("beans.full")) { write(resp, HttpMonitors.beansName()); return; } } private List> filter(List> list, HttpServletRequest req) { if (list == null || list.isEmpty() || AppInfo.getBoolean("sumk.acts.search.disable", false)) { return list; } Set keys = new HashSet<>(); for (Map map : list) { keys.addAll(map.keySet()); } Map params = this.getFilterParams(req, keys); if (params.isEmpty()) { return list; } List> ret = new ArrayList<>(list.size()); for (Map map : list) { if (contain(map, params)) { ret.add(map); } } return ret; } private Map getFilterParams(HttpServletRequest req, Collection keys) { Map map = new HashMap<>(); for (String key : keys) { String v = req.getParameter("_" + key); if (v != null) { v = v.toLowerCase().trim(); } if (StringUtil.isNotEmpty(v)) { map.put(key, v); } } return map; } private boolean contain(Map source, Map params) { for (String key : params.keySet()) { Object obj = source.get(key); if (obj == null) { return false; } String v = S.json().toJson(obj); if (!v.toLowerCase().contains(params.get(key))) { return false; } } return true; } private void write(HttpServletResponse resp, String msg) throws IOException { if (msg == null) { return; } resp.getOutputStream().write(msg.getBytes(AppInfo.UTF8)); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/HttpMethod.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; /** * 只有这里定义的方法,才能在@Web中使用 */ public final class HttpMethod { public static final String POST = "POST"; public static final String GET = "GET"; public static final String PUT = "PUT"; public static final String DELETE = "DELETE"; public static final String PATCH = "PATCH"; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/MultipartServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; import javax.servlet.annotation.MultipartConfig; import org.yx.annotation.Bean; import org.yx.annotation.http.SumkServlet; import org.yx.common.locale.I18n; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.handler.HttpHandlerChain; import org.yx.http.handler.MultipartHolder; import org.yx.http.handler.WebContext; /** * * @author Administrator */ @Bean @SumkServlet(path = { "/upload/*" }, loadOnStartup = -1, appKey = "upload") @MultipartConfig(location = "", maxFileSize = -1L, maxRequestSize = -1L, fileSizeThreshold = -1) public class MultipartServer extends AbstractActionServer { private static final long serialVersionUID = 1L; private static final String MULTIPART_FORMDATA = "multipart/form-data"; @Override protected void handle(WebContext wc) throws Throwable { if (HttpHandlerChain.multipart == null) { log.error("上传功能被禁用"); throw BizException.create(HttpErrorCode.UPLOAD_DISABLED, "上传功能暂时无法使用"); } String contextType = wc.httpRequest().getContentType(); if (contextType == null || !contextType.startsWith(MULTIPART_FORMDATA)) { log.error("the MIME of " + wc.rawAct() + " act is " + MULTIPART_FORMDATA + ",not " + contextType); throw BizException.create(HttpErrorCode.UPLOAD_NOT_MULTI_TYPE, I18n.get("sumk.http.upload.error.contentType", "该接口的ContentType不是" + MULTIPART_FORMDATA)); } try { HttpHandlerChain.multipart.handle(wc); } finally { MultipartHolder.remove(); } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/RestServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; import org.yx.annotation.Bean; import org.yx.annotation.http.SumkServlet; import org.yx.http.handler.HttpHandlerChain; import org.yx.http.handler.WebContext; @Bean @SumkServlet(path = { "/rest/*" }, loadOnStartup = 1, appKey = "rest") public class RestServer extends AbstractActionServer { private static final long serialVersionUID = 7437235491L; @Override protected void handle(WebContext wc) throws Throwable { HttpHandlerChain.rest.handle(wc); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/server/SumkMonitor.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.server; import static org.yx.conf.Const.LN; import java.io.IOException; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.concurrent.ThreadPoolExecutor; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.annotation.Bean; import org.yx.annotation.http.SumkServlet; import org.yx.base.sumk.UnsafeStringWriter; import org.yx.common.action.ActionStatis; import org.yx.common.action.StatisItem; import org.yx.common.monitor.Monitors; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.http.act.HttpActionInfo; import org.yx.http.act.HttpActions; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.monitor.HttpMonitors; import org.yx.http.user.AbstractUserSession; import org.yx.http.user.SessionObject; import org.yx.http.user.TimedCachedObject; import org.yx.http.user.UserSession; import org.yx.http.user.WebSessions; import org.yx.util.StringUtil; import org.yx.util.SumkDate; import org.yx.util.SumkThreadPool; @Bean @SumkServlet(path = { "/_sumk_monitor/*" }, loadOnStartup = -1, appKey = "sumkMonitor") public class SumkMonitor extends AbstractCommonHttpServlet { private static final long serialVersionUID = 2364534491L; private static final String TYPE_SPLIT = "\n\n\n"; @Override protected void handle(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (!InnerHttpUtil.preServerHandle(req, resp, "sumk.http.monitor")) { return; } UnsafeStringWriter writer = new UnsafeStringWriter(512); this.outputServerInfo(req, writer); this.outputStatis(req, writer); this.outputNoVisit(req, writer); this.dbVisitInfo(req, writer); this.outputBeans(req, writer); this.outputSystem(req, writer); this.outputJvmInfo(req, writer); this.outputGcInfo(req, writer); this.outputAllTrack(req, writer); this.outputThreadPool(req, writer); this.outputLogLevels(req, writer); this.outputLocalSessions(req, writer); this.outputAppInfo(req, writer); this.outputDataSource(req, writer); this.outputSumkDate(req, writer); this.outputRpcDatas(req, writer); writer.flush(); String ret = writer.toString(); resp.getOutputStream().write(ret.getBytes(StandardCharsets.UTF_8)); } private String localSessions(boolean full) { UserSession userSession = WebSessions.userSession(); if (userSession == null) { return ""; } StringBuilder sb = new StringBuilder("##localSessions:").append(" ").append(userSession.localCacheSize()); if (full && (userSession instanceof AbstractUserSession)) { AbstractUserSession us = (AbstractUserSession) userSession; boolean fullKey = AppInfo.getBoolean("sumk.http.monitor.session.fullkey", false); for (Entry en : us.localCache().entrySet()) { String sessionId = en.getKey(); if (!fullKey) { int mark = sessionId.length() / 2; sessionId = "**" + sessionId.substring(mark); } sb.append(AppInfo.LN).append(sessionId).append(" : ") .append(SumkDate.of(en.getValue().getRefreshTime())).append(" ") .append(S.json().fromJson(en.getValue().getJson(), SessionObject.class).getUserId()); } } return sb.toString(); } private void outputLocalSessions(HttpServletRequest req, Writer writer) throws IOException { String local = req.getParameter("localSessions"); if ("1".equals(local)) { writer.append(localSessions(false)); } else if ("full".equals(local)) { writer.append(localSessions(true)); } else { return; } writer.append(TYPE_SPLIT); } private void outputServerInfo(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("server"))) { return; } writer.append(HttpMonitors.serverInfo()); writer.append(TYPE_SPLIT); } private void outputBeans(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("beans"))) { return; } List names = HttpMonitors.beans(); StringBuilder sb = new StringBuilder().append("##beans:").append(names.size()).append(Const.LN); for (String name : names) { sb.append(name).append(Const.LN); } writer.append(sb.toString()); writer.append(TYPE_SPLIT); } private void outputStatis(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("statis"))) { return; } ActionStatis actStatic = InnerHttpUtil.getActionStatis(); boolean needReset = "1".equals(req.getParameter("statis.reset")) && AppInfo.getBoolean("sumk.http.statis.reset.allow", true); Map map = needReset ? actStatic.getAndReset() : actStatic.getAll(); List values = new ArrayList<>(map.values()); values.sort((a, b) -> Long.compare(b.getSuccessTime(), a.getSuccessTime())); long totalSuccessCount = 0; long totalSuccessTime = 0; long totalFailCount = 0; long totalFailTime = 0; boolean onlyfailed = "1".equals(req.getParameter("statis.onlyfailed")); StringBuilder sb = new StringBuilder("##").append(StatisItem.header()).append(LN); for (StatisItem v : values) { if (onlyfailed && v.getFailedCount() == 0) { continue; } sb.append(v.toSimpleString()).append(LN); totalSuccessCount += v.getSuccessCount(); totalSuccessTime += v.getSuccessTime(); totalFailCount += v.getFailedCount(); totalFailTime += v.getFailedTime(); } long c = totalSuccessCount; long t = totalSuccessTime; double avg = c == 0 ? 0 : t * 1d / c; String total = String.join(" ", "**TOTAL**", String.valueOf(c), String.valueOf(t), String.valueOf(Math.round(avg)), String.valueOf(totalFailCount), String.valueOf(totalFailTime)); sb.append(total).append(LN); writer.append(sb.toString()); writer.append(TYPE_SPLIT); } private void outputNoVisit(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("noVisit"))) { return; } Collection all = HttpActions.actions(); Map visited = InnerHttpUtil.getActionStatis().getAll(); StringBuilder sb = new StringBuilder(500).append("##total(不含login): ").append(all.size()).append(" ") .append("visited(含login): ").append(visited.size()).append(LN); for (HttpActionInfo info : all) { if (visited.containsKey(info.rawAct())) { continue; } sb.append(info.rawAct()).append(" ").append(info.node().cnName()).append(LN); } writer.append(sb.toString()); writer.append(TYPE_SPLIT); } private void outputSystem(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("system"))) { return; } writer.append(HttpMonitors.systemInfo()); writer.append(TYPE_SPLIT); } private void outputJvmInfo(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("jvm"))) { return; } writer.append(HttpMonitors.jvmInfo()); writer.append(TYPE_SPLIT); } private void outputAllTrack(HttpServletRequest req, Writer writer) throws IOException { String stack = req.getParameter("stack"); if ("1".equals(stack)) { writer.append(HttpMonitors.stack(false)); } else if ("full".equals(stack)) { writer.append(HttpMonitors.stack(true)); } else { return; } writer.append(TYPE_SPLIT); } private void outputThreadPool(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("threadpool"))) { return; } writer.append(HttpMonitors.threadPoolInfo((ThreadPoolExecutor) SumkThreadPool.executor())); writer.append(TYPE_SPLIT); } private void outputLogLevels(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("logLevel"))) { return; } writer.append(HttpMonitors.logLevels()); writer.append(TYPE_SPLIT); } private void outputAppInfo(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("appinfo")) || !AppInfo.getBoolean("sumk.appinfo.monitor", false)) { return; } StringBuilder sb = new StringBuilder(); new TreeMap<>(AppInfo.subMap("")).forEach((k, v) -> { sb.append(k).append(" = ").append(v.replace(LN, "\\n")).append(LN); }); writer.append(sb.toString()); writer.append(TYPE_SPLIT); } private void dbVisitInfo(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("db.cache"))) { return; } Object ret = Monitors.getMessage("monitor", "db.cache", null); if (ret == null) { return; } writer.append(ret.toString()); writer.append(TYPE_SPLIT); } private void outputDataSource(HttpServletRequest req, Writer writer) throws IOException { String ds = req.getParameter("datasource"); if (StringUtil.isEmpty(ds)) { return; } Object ret = Monitors.getMessage("monitor", "datasource", ds); if (ret == null) { return; } writer.append(ret.toString()); writer.append(TYPE_SPLIT); } private void outputSumkDate(HttpServletRequest req, UnsafeStringWriter writer) { if (!"1".equals(req.getParameter("sumkdate"))) { return; } writer.append(HttpMonitors.sumkDateCacheChangeCount()); writer.append(TYPE_SPLIT); } private void outputGcInfo(HttpServletRequest req, UnsafeStringWriter writer) { if (!"1".equals(req.getParameter("gc"))) { return; } writer.append(HttpMonitors.gcInfo()); writer.append(TYPE_SPLIT); } private void outputRpcDatas(HttpServletRequest req, Writer writer) throws IOException { if (!"1".equals(req.getParameter("rpcData"))) { return; } Object ret = Monitors.getMessage("monitor", "rpcData", null); if (ret == null) { return; } writer.append(ret.toString()); writer.append(TYPE_SPLIT); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/spec/HttpSpecs.java ================================================ package org.yx.http.spec; import java.lang.reflect.Method; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import org.yx.annotation.http.SumkFilter; import org.yx.annotation.http.SumkServlet; import org.yx.annotation.http.Upload; import org.yx.annotation.http.Web; import org.yx.annotation.spec.Specs; public class HttpSpecs extends Specs { private static BiFunction webParser = (bean, m) -> { Web web = m.getAnnotation(Web.class); if (web == null) { return null; } return new WebSpec(web.value(), web.cnName(), web.requireLogin(), web.requestType(), web.sign(), web.responseType(), web.tags(), web.toplimit(), web.method()); }; private static BiFunction uploadParser = (bean, m) -> { Upload c = m.getAnnotation(Upload.class); if (c == null) { return null; } return new UploadSpec(c.paramName()); }; private static Function, SumkServletSpec> sumkServletParser = clz -> { SumkServlet f = clz.getAnnotation(SumkServlet.class); if (f == null) { return null; } return new SumkServletSpec(f.name(), f.path(), f.loadOnStartup(), f.asyncSupported(), f.appKey()); }; private static Function, SumkFilterSpec> sumkFilterParser = clz -> { SumkFilter f = clz.getAnnotation(SumkFilter.class); if (f == null) { return null; } return new SumkFilterSpec(f.name(), f.path(), f.dispatcherType(), f.isMatchAfter(), f.asyncSupported()); }; public static Function, SumkFilterSpec> getSumkFilterParser() { return sumkFilterParser; } public static Function, SumkServletSpec> getSumkServletParser() { return sumkServletParser; } public static BiFunction getUploadParser() { return uploadParser; } public static BiFunction getWebParser() { return webParser; } public static void setSumkFilterParser(Function, SumkFilterSpec> sumkFilterParser) { HttpSpecs.sumkFilterParser = Objects.requireNonNull(sumkFilterParser); } public static void setSumkServletParser(Function, SumkServletSpec> sumkServletParser) { HttpSpecs.sumkServletParser = Objects.requireNonNull(sumkServletParser); } public static void setUploadParser(BiFunction uploadParser) { HttpSpecs.uploadParser = Objects.requireNonNull(uploadParser); } public static void setWebParser(BiFunction webParser) { HttpSpecs.webParser = Objects.requireNonNull(webParser); } public static WebSpec extractWeb(Object bean, Method m) { return parse(bean, m, webParser); } public static UploadSpec extractUpload(Object bean, Method m) { return parse(bean, m, uploadParser); } public static SumkServletSpec extractSumkServlet(Class clz) { return parse(clz, sumkServletParser); } public static SumkFilterSpec extractSumkFilter(Class clz) { return parse(clz, sumkFilterParser); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/spec/SumkFilterSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.spec; import javax.servlet.DispatcherType; public class SumkFilterSpec { private final String name; private final String[] path; private final DispatcherType[] dispatcherType; private final boolean isMatchAfter; private final boolean asyncSupported; public SumkFilterSpec(String name, String[] path, DispatcherType[] dispatcherType, boolean isMatchAfter, boolean asyncSupported) { this.name = name; this.path = path; this.dispatcherType = dispatcherType; this.isMatchAfter = isMatchAfter; this.asyncSupported = asyncSupported; } public String name() { return this.name; } public String[] path() { return this.path; } public boolean asyncSupported() { return this.asyncSupported; } public DispatcherType[] dispatcherType() { return dispatcherType; } public boolean isMatchAfter() { return isMatchAfter; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/spec/SumkServletSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.spec; import java.util.Objects; public class SumkServletSpec { private final String name; private final String[] path; private final int loadOnStartup; private final boolean asyncSupported; private final String appKey; public SumkServletSpec(String name, String[] path, int loadOnStartup, boolean asyncSupported, String appKey) { this.name = name; this.path = Objects.requireNonNull(path); this.loadOnStartup = loadOnStartup; this.asyncSupported = asyncSupported; this.appKey = appKey; } public String name() { return this.name; } public String[] path() { return this.path; } public int loadOnStartup() { return this.loadOnStartup; } public boolean asyncSupported() { return this.asyncSupported; } public String appKey() { return this.appKey; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/spec/UploadSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.spec; import java.util.Objects; public class UploadSpec { private final String paramName; public UploadSpec(String paramName) { this.paramName = Objects.requireNonNull(paramName); } public String paramName() { return this.paramName; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/spec/WebSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.spec; import java.util.Objects; import org.yx.http.MessageType; public class WebSpec { private final String value; private final String cnName; private final boolean requireLogin; private final MessageType requestType; private final boolean sign; private final MessageType responseType; private final String[] tags; private final int toplimit; private final String[] method; public WebSpec(String value, String cnName, boolean requireLogin, MessageType requestType, boolean sign, MessageType responseType, String[] tags, int toplimit, String[] method) { this.value = Objects.requireNonNull(value); this.cnName = Objects.requireNonNull(cnName); this.requireLogin = requireLogin; this.requestType = Objects.requireNonNull(requestType); this.sign = sign; this.responseType = Objects.requireNonNull(responseType); this.tags = Objects.requireNonNull(tags); this.toplimit = toplimit; this.method = Objects.requireNonNull(method); } public String value() { return this.value; } public String cnName() { return this.cnName; } public boolean requireLogin() { return this.requireLogin; } public MessageType requestType() { return this.requestType; } public boolean sign() { return this.sign; } public MessageType responseType() { return this.responseType; } public String[] tags() { return this.tags; } public int toplimit() { return this.toplimit; } public String[] method() { return this.method; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/start/JettyHandlerSupplier.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.start; import java.util.Objects; import java.util.function.Supplier; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.server.session.SessionHandler; import org.yx.conf.AppInfo; public class JettyHandlerSupplier { private static Supplier gzipHandlerSupplier = () -> { GzipHandler h = new GzipHandler(); h.addIncludedMethods("POST"); h.setMinGzipSize(AppInfo.getInt("sumk.webserver.gzip.minsize", 1000)); return h; }; private static Supplier resourceHandlerSupplier = () -> { ResourceHandler handler = new ResourceHandler(); String welcomes = AppInfo.get("sumk.webserver.resource.welcomes"); if (welcomes != null && welcomes.length() > 0) { handler.setWelcomeFiles(welcomes.replace(',', ',').split(",")); } return handler; }; private static Supplier sessionHandlerSupplier = SessionHandler::new; public static Supplier gzipHandlerSupplier() { return gzipHandlerSupplier; } public static void setGzipHandlerSupplier(Supplier h) { JettyHandlerSupplier.gzipHandlerSupplier = Objects.requireNonNull(h); } public static Supplier resourceHandlerSupplier() { return resourceHandlerSupplier; } public static void setResourceHandlerSupplier(Supplier h) { JettyHandlerSupplier.resourceHandlerSupplier = Objects.requireNonNull(h); } public static Supplier sessionHandlerSupplier() { return sessionHandlerSupplier; } public static void setSessionHandlerSupplier(Supplier h) { JettyHandlerSupplier.sessionHandlerSupplier = Objects.requireNonNull(h); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/start/JettyHttpsServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.start; import java.io.File; import java.net.URISyntaxException; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.util.FileUtil; public class JettyHttpsServer extends JettyServer { public JettyHttpsServer(int port) { super(port); } @Override protected ConnectionFactory[] getConnectionFactorys() throws URISyntaxException { @SuppressWarnings("deprecation") SslContextFactory sslContextFactory = new SslContextFactory(); String path = get(Const.KEY_STORE_PATH); File keystoreFile = FileUtil.file(path); if (!keystoreFile.exists()) { String msg = path + " is not exist"; Logs.http().error(msg); throw new SumkException(2345345, msg); } sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); sslContextFactory.setKeyStorePassword(get("sumk.webserver.ssl.storePassword")); sslContextFactory.setKeyManagerPassword(get("sumk.webserver.ssl.managerPassword")); sslContextFactory.setCertAlias(get("sumk.webserver.ssl.alias")); String v = AppInfo.get("sumk.webserver.ssl.storeType", null); if (v != null) { sslContextFactory.setKeyStoreType(v); } sslContextFactory.setTrustAll(AppInfo.getBoolean("sumk.webserver.ssl.trustAll", false)); Logs.http().info("using https"); return new ConnectionFactory[] { new SslConnectionFactory(sslContextFactory, AppInfo.get("sumk.webserver.ssl.protocol", "http/1.1")), new HttpConnectionFactory() }; } private String get(String name) { String v = AppInfo.get(name, null); if (v == null) { throw new SumkException(12391763, name + " is null!!! please set it"); } return v; } @Override protected ServerConnector createConnector() throws Exception { ServerConnector connector = super.createConnector(); connector.setDefaultProtocol(AppInfo.get("sumk.webserver.ssl.protocol", "SSL")); return connector; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/start/JettyServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.start; import java.util.Arrays; import java.util.EventListener; import java.util.List; import javax.servlet.ServletContextListener; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandler.ContextScopeListener; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.thread.ExecutorThreadPool; import org.yx.base.Lifecycle; import org.yx.base.context.AppContext; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.main.SumkServer; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class JettyServer implements Lifecycle { protected final int port; protected Server server; private boolean started = false; public JettyServer(int port) { this.port = port; this.init(); } protected ConnectionFactory[] getConnectionFactorys() throws Exception { return new ConnectionFactory[] { new HttpConnectionFactory() }; } protected ServerConnector createConnector() throws Exception { ServerConnector connector = new JettyServerConnector(server, null, null, null, AppInfo.getInt("sumk.webserver.connector.acceptors", -1), AppInfo.getInt("sumk.webserver.connector.selectors", -1), getConnectionFactorys()); connector.setReuseAddress(AppInfo.getBoolean("sumk.webserver.reuseAddr", false)); int backlog = AppInfo.getInt("sumk.webserver.backlog", 0); if (backlog > 0) { connector.setAcceptQueueSize(backlog); } return connector; } protected synchronized void init() { try { buildJettyProperties(); server = new Server(new ExecutorThreadPool(SumkServer.getHttpExecutor())); ServerConnector connector = this.createConnector(); Logs.http().info("http listen at port: {}", port); String host = SumkServer.httpHost(); if (host != null && host.length() > 0) { connector.setHost(host); } connector.setPort(port); server.setConnectors(new Connector[] { connector }); ServletContextHandler context = createServletContextHandler(); context.setContextPath(AppInfo.get("sumk.webserver.root", "sumk.jetty.web.root", "/")); context.addEventListener(new SumkLoaderListener()); addUserListener(context, Arrays.asList(ServletContextListener.class, ContextScopeListener.class)); String resourcePath = AppInfo.get("sumk.webserver.resource"); if (StringUtil.isNotEmpty(resourcePath)) { ResourceHandler resourceHandler = JettyHandlerSupplier.resourceHandlerSupplier().get(); if (resourceHandler != null) { resourceHandler.setResourceBase(resourcePath); context.insertHandler(resourceHandler); } } if (AppInfo.getBoolean("sumk.webserver.session.enable", false)) { SessionHandler h = JettyHandlerSupplier.sessionHandlerSupplier().get(); if (h != null) { context.insertHandler(h); } } addUserHandlers(context); server.setHandler(context); } catch (Throwable e) { Logs.http().error(e.getLocalizedMessage(), e); AppContext.startFailed(); } } protected void addUserHandlers(ServletContextHandler context) { Object obj = AppContext.inst().get(HandlerWrapper.class); if (obj instanceof HandlerWrapper[]) { HandlerWrapper[] hs = (HandlerWrapper[]) obj; for (HandlerWrapper h : hs) { Logs.http().info("add jetty handler {}", h); context.insertHandler(h); } } } protected void buildJettyProperties() { String key = "org.eclipse.jetty.server.Request.maxFormContentSize"; String v = AppInfo.get(key); if (v != null && v.length() > 0 && System.getProperty(key) == null) { System.setProperty(key, v); } } @Override public synchronized void start() { if (started || server == null) { return; } try { server.start(); started = true; } catch (Throwable e) { Logs.http().error(e.getLocalizedMessage(), e); throw new SumkException(234234, "jetty启动失败"); } } /** * @return */ private ServletContextHandler createServletContextHandler() { ServletContextHandler handler = IOC.get(ServletContextHandler.class); if (handler != null) { return handler; } handler = new ServletContextHandler(); String welcomes = AppInfo.get("sumk.webserver.welcomes"); if (welcomes != null && welcomes.length() > 0) { handler.setWelcomeFiles(welcomes.replace(',', ',').split(",")); } GzipHandler gzipHandler = JettyHandlerSupplier.gzipHandlerSupplier().get(); if (gzipHandler != null) { handler.setGzipHandler(gzipHandler); } return handler; } private void addUserListener(ServletContextHandler context, List> intfs) { for (Class intf : intfs) { List listeners = IOC.getBeans(intf); if (CollectionUtil.isEmpty(listeners)) { continue; } for (EventListener lis : listeners) { context.addEventListener(lis); } } } @Override public void stop() { if (server != null) { try { server.stop(); this.started = false; } catch (Exception e) { Log.printStack("sumk.http", e); } } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/start/JettyServerConnector.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.start; import java.io.IOException; import java.nio.channels.ServerSocketChannel; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.thread.Scheduler; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Logs; public class JettyServerConnector extends ServerConnector { public JettyServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, int acceptors, int selectors, ConnectionFactory[] factories) { super(server, executor, scheduler, bufferPool, acceptors, selectors, factories); } @Override protected ServerSocketChannel openAcceptChannel() throws IOException { IOException ex = null; try { return super.openAcceptChannel(); } catch (IOException e) { ex = e; } for (int i = 0; i < AppInfo.getInt("sumk.webserver.bind.retry", 100); i++) { try { Thread.sleep(AppInfo.getLong("sumk.webserver.bind.sleepTime", 2000)); } catch (InterruptedException e1) { Logs.http().error("showdown because of InterruptedException"); Thread.currentThread().interrupt(); throw new SumkException(34534560, "收到中断." + e1); } Logs.http().warn("{} was occupied({}),begin retry {}", this.getPort(), ex.getMessage(), i); try { return super.openAcceptChannel(); } catch (IOException e) { if (isInheritChannel()) { throw e; } ex = e; } } throw ex; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/start/SumkLoaderListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.start; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.EventListener; import java.util.List; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterRegistration; import javax.servlet.MultipartConfigElement; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletRegistration; import javax.servlet.annotation.MultipartConfig; import org.yx.bean.BeanKit; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.http.kit.HttpSettings; import org.yx.http.spec.HttpSpecs; import org.yx.http.spec.SumkFilterSpec; import org.yx.http.spec.SumkServletSpec; import org.yx.http.user.HttpLoginWrapper; import org.yx.http.user.LoginServlet; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.main.StartConstants; import org.yx.main.SumkServer; import org.yx.util.CollectionUtil; import org.yx.util.Loader; import org.yx.util.StringUtil; /** * 如果使用tomcat等外部容器启动sumk,需要将sumk.webserver.disable设为1,并在web.xml中添加:
* <listener>
*    * <listener-class>org.yx.main.SumkLoaderListener</listener-class> *
* </listener> * * @author 游夏 * */ public class SumkLoaderListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { Logs.http().debug("contextInitialized"); try { SumkServer.start(null, Collections.singleton(StartConstants.EMBED_WEBSERVER_DISABLE)); if (!SumkServer.isHttpEnable()) { return; } ServletContext context = sce.getServletContext(); addFilters(context); addServlets(context); String path = AppInfo.get("sumk.http.login.path", "/login/*"); if (IOC.getBeans(LoginServlet.class).size() > 0) { String loginPath = path; if (!loginPath.startsWith("/")) { loginPath = "/" + loginPath; } Logs.http().info("login path:{}", context.getContextPath() + loginPath); ServletRegistration.Dynamic dynamic = context.addServlet(loginPath, HttpLoginWrapper.class); dynamic.addMapping(loginPath); dynamic.setLoadOnStartup(1); } addListeners(context); } catch (Exception e) { Logs.http().error(e.getMessage(), e); throw e; } } private void addServlets(ServletContext context) { List servlets = IOC.getBeans(Servlet.class); for (Servlet bean : servlets) { Class targetClz = BeanKit.getTargetClass(bean); SumkServletSpec sumk = HttpSpecs.extractSumkServlet(targetClz); if (sumk == null) { continue; } String name = sumk.name(); if (name == null || name.isEmpty()) { name = bean.getClass().getSimpleName(); } ServletRegistration.Dynamic dynamic = context.addServlet(name, bean); dynamic.setAsyncSupported(sumk.asyncSupported()); dynamic.setLoadOnStartup(sumk.loadOnStartup()); String[] values = sumk.path(); String value = null; if (StringUtil.isNotEmpty(sumk.appKey()) && (value = AppInfo.get("sumk.http.servlet." + sumk.appKey())) != null) { values = StringUtil.toLatin(value).split(","); } dynamic.addMapping(values); if (HttpSettings.isUploadEnable()) { this.handleUpload(dynamic, targetClz, values); } Logs.http().trace("add web mapping : {} - {} -{}", name, bean.getClass().getSimpleName(), Arrays.toString(values)); } } private void handleUpload(ServletRegistration.Dynamic dynamic, Class targetClz, String[] paths) { try { MultipartConfig mc = targetClz.getAnnotation(MultipartConfig.class); if (mc == null) { return; } String location = mc.location(); if (StringUtil.isEmpty(location)) { location = AppInfo.get("sumk.http.multipart.location", null); if (location == null) { Path p = Files.createTempDirectory("multi"); location = p.toFile().getAbsolutePath(); } } long maxFileSize = mc.maxFileSize() > 0 ? mc.maxFileSize() : AppInfo.getLong("sumk.http.multipart.maxFileSize", 1024L * 1024 * 10); long maxRequestSize = mc.maxRequestSize() > 0 ? mc.maxRequestSize() : AppInfo.getLong("sumk.http.multipart.maxRequestSize", 1024L * 1024 * 50); int fileSizeThreshold = mc.fileSizeThreshold() > 0 ? mc.fileSizeThreshold() : AppInfo.getInt("sumk.http.multipart.fileSizeThreshold", 1024 * 10); MultipartConfigElement c = new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold); dynamic.setMultipartConfig(c); if (Logs.http().isInfoEnabled()) { Logs.http().info("{} location={},maxFileSize={},maxRequestSize={},fileSizeThreshold={}", Arrays.toString(paths), location, maxFileSize, maxRequestSize, fileSizeThreshold); } } catch (Throwable e) { Logs.http().warn("不支持文件上传!!!", e); } } private void addFilters(ServletContext context) { List filters = IOC.getBeans(Filter.class); if (org.yx.util.CollectionUtil.isEmpty(filters)) { return; } for (Filter bean : filters) { Class targetClz = BeanKit.getTargetClass(bean); SumkFilterSpec sumk = HttpSpecs.extractSumkFilter(targetClz); if (sumk == null) { continue; } String name = sumk.name(); if (name.isEmpty()) { name = bean.getClass().getSimpleName(); } Logs.http().trace("add web filter : {} - {}", name, bean.getClass().getSimpleName()); FilterRegistration.Dynamic r = context.addFilter(name, bean); r.setAsyncSupported(sumk.asyncSupported()); DispatcherType[] type = sumk.dispatcherType(); EnumSet types = null; if (type.length > 0) { types = EnumSet.copyOf(Arrays.asList(type)); } r.addMappingForUrlPatterns(types, sumk.isMatchAfter(), sumk.path()); } } private static InputStream openResourceAsStream(String name) { InputStream in = Loader.getResourceAsStream(name + "-impl"); if (in != null) { return in; } return Loader.getResourceAsStream(name); } private void addListeners(ServletContext context) { try { InputStream in = openResourceAsStream("META-INF/http/listeners"); addListener(context, CollectionUtil.loadList(in)); } catch (Exception e) { Log.printStack("sumk.error", e); return; } } private void addListener(ServletContext context, List intfs) throws ClassNotFoundException { for (String intf : intfs) { Class clz = Loader.loadClass(intf); if (!EventListener.class.isAssignableFrom(clz)) { Logs.http().info(intf + " is not implement EventListener"); continue; } @SuppressWarnings("unchecked") List listeners = (List) IOC.getBeans(clz); if (org.yx.util.CollectionUtil.isEmpty(listeners)) { continue; } for (EventListener lis : listeners) { Logs.http().trace("add web listener:{}", lis.getClass().getSimpleName()); context.addListener(lis); } } } @Override public void contextDestroyed(ServletContextEvent sce) { SumkServer.destroy(); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/start/WebAnnotationResolver.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.start; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.bean.BeanKit; import org.yx.bean.aop.asm.AsmUtils; import org.yx.bean.aop.asm.MethodParamInfo; import org.yx.bean.aop.asm.ParamPojos; import org.yx.common.StringEntity; import org.yx.conf.AppInfo; import org.yx.http.act.HttpActionNode; import org.yx.http.spec.HttpSpecs; import org.yx.http.spec.WebSpec; import org.yx.log.Logs; import org.yx.util.StringUtil; public class WebAnnotationResolver { private Predicate exclude = BooleanMatcher.FALSE; private List rawNames(Method m, WebSpec web) { String name = web.value(); if (name == null || name.isEmpty()) { return Collections.singletonList(m.getName()); } List list = new ArrayList(1); String[] names = StringUtil.toLatin(name).split(","); for (String raw : names) { if (raw != null && (raw = raw.trim()).length() > 0) { list.add(raw); } } return list.size() > 0 ? list : Collections.singletonList(m.getName()); } public WebAnnotationResolver() { String patterns = AppInfo.get("sumk.http.exclude", null); if (patterns != null) { this.exclude = Matchers.createWildcardMatcher(patterns, 1); Logs.http().debug("web exclude:{}", this.exclude); } } public List> resolve(Object bean) throws Exception { Class clz = BeanKit.getTargetClass(bean); if (exclude.test(clz.getName())) { return null; } Method[] methods = clz.getMethods(); List httpMethods = new ArrayList<>(); for (final Method m : methods) { if (HttpSpecs.extractWeb(bean, m) == null) { continue; } if (AsmUtils.notPublicOnly(m.getModifiers())) { Logs.http().warn("$$$ {}.{} has bad modifiers, maybe static or private", clz.getName(), m.getName()); continue; } httpMethods.add(m); } if (httpMethods.isEmpty()) { return null; } List mpInfos = AsmUtils.buildMethodInfos(httpMethods); List> infos = new ArrayList<>(mpInfos.size() * 2); for (MethodParamInfo info : mpInfos) { Method m = info.getMethod(); WebSpec act = HttpSpecs.extractWeb(bean, m); HttpActionNode node = new HttpActionNode(bean, m, ParamPojos.create(info), act); List names = rawNames(m, act); if (names == null || names.isEmpty()) { continue; } for (String name : names) { infos.add(StringEntity.create(name, node)); } } return infos; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/AbstractLoginServlet.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.yx.base.context.ActionContext; import org.yx.base.sumk.UnsafeByteArrayOutputStream; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.http.HttpHeaderName; import org.yx.http.kit.HttpSettings; import org.yx.http.kit.InnerHttpUtil; import org.yx.http.log.HttpLogs; import org.yx.log.Logs; import org.yx.util.StringUtil; import org.yx.util.UUIDSeed; public abstract class AbstractLoginServlet implements LoginServlet { private static final String LOGIN_NAME = "*login*"; private static final String NEW_SESSION_ID = "sumk.http.session.id"; private UserSession session; protected Logger logger() { return Logs.http(); } @Override public void service(HttpServletRequest req, HttpServletResponse resp) { final long beginTime = System.currentTimeMillis(); Throwable ex = null; String user = null; Charset charset = InnerHttpUtil.charset(req); try { InnerHttpUtil.startContext(req, resp, LOGIN_NAME); if (!acceptMethod(req, resp)) { logger().warn("不是login的有效method,比如HEAD等方法可能不支持"); return; } InnerHttpUtil.setRespHeader(resp, charset); String sid = createSessionId(req); user = getUserName(req); LoginObject obj = login(sid, user, req); if (obj == null) { logger().warn("{} 没有关联到session对象", sid); InnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, "登录失败", charset); return; } if (obj.getErrorMsg() != null) { InnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, obj.getErrorMsg(), charset); return; } if (obj.getSessionObject() == null || StringUtil.isEmpty(obj.getSessionObject().getUserId())) { logger().warn("{} :sid:{} 未正确设置session对象,或者session对象中的userId为空", user, sid); InnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, "登录失败", charset); return; } Object newSid = req.getAttribute(NEW_SESSION_ID); if (newSid instanceof String) { logger().debug("change sid {} to {}", sid, newSid); sid = (String) newSid; } kickout(obj, sid, req, resp); byte[] key = createEncryptKey(req); if (!this.setSession(req, sid, obj, key)) { logger().warn("{} :sid:{} login failed,maybe sessionId existed.", user, sid); InnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, "登录失败", charset); return; } String userId = obj.getSessionObject().getUserId(); resp.setHeader(HttpHeaderName.sessionId(), sid); if (StringUtil.isNotEmpty(userId)) { resp.setHeader(HttpHeaderName.userFlag(), userId); } if (HttpSettings.isCookieEnable()) { String contextPath = req.getContextPath(); if (!contextPath.startsWith("/")) { contextPath = "/" + contextPath; } String attr = ";Path=".concat(contextPath); setSessionCookie(req, resp, sid, attr); setUserFlagCookie(req, resp, userId, attr); } UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream(64); this.outputKey(out, key, req, resp); if (obj.getResponseData() != null) { out.write(obj.getResponseData().getBytes(charset)); } resp.getOutputStream().write(out.toByteArray()); } catch (Throwable e) { ex = e; logger().error("user:" + user + ",message:" + e.getLocalizedMessage(), e); if (e instanceof BizException) { BizException be = (BizException) e; InnerHttpUtil.sendError(resp, be.getCode(), be.getMessage(), charset); } else { InnerHttpUtil.sendError(resp, HttpErrorCode.LOGINFAILED, "login fail:" + user, charset); } } finally { long time = System.currentTimeMillis() - beginTime; HttpLogs.log(null, req, ex, time); InnerHttpUtil.record(LOGIN_NAME, time, ex == null); ActionContext.remove(); } } protected void kickout(LoginObject obj, String sid, HttpServletRequest req, HttpServletResponse resp) { } protected boolean setSession(HttpServletRequest req, String sessionId, LoginObject obj, byte[] key) { if (req.getAttribute(NEW_SESSION_ID) instanceof String) { return true; } return this.session.setSession(sessionId, obj.getSessionObject(), key, HttpSettings.isSingleLogin()); } protected boolean acceptMethod(HttpServletRequest req, HttpServletResponse resp) throws IOException { if (!HttpSettings.defaultHttpMethods().contains(req.getMethod())) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, req.getMethod() + " not allowd"); return false; } return true; } protected String getUserName(HttpServletRequest req) { return req.getParameter(AppInfo.get("sumk.http.login.username", "username")); } protected void setSessionCookie(HttpServletRequest req, HttpServletResponse resp, final String sid, String attr) { StringBuilder cookie = new StringBuilder(64).append(HttpHeaderName.sessionId()).append('=').append(sid) .append(attr); resp.addHeader("Set-Cookie", cookie.toString()); } protected void setUserFlagCookie(HttpServletRequest req, HttpServletResponse resp, String userId, String attr) { if (HttpSettings.isSingleLogin() && StringUtil.isNotEmpty(userId)) { StringBuilder cookie = new StringBuilder(); cookie.append(HttpHeaderName.userFlag()).append('=').append(userId).append(attr); resp.addHeader("Set-Cookie", cookie.toString()); } } protected void outputKey(OutputStream out, byte[] key, HttpServletRequest req, HttpServletResponse resp) throws IOException { if (AppInfo.getBoolean("sumk.http.key.output.body", false)) { out.write(S.base64().encode(key)); out.write(new byte[] { '\t', '\n' }); } if (AppInfo.getBoolean("sumk.http.key.output.header", true)) { resp.setHeader(AppInfo.get("sumk.http.header.skey", "skey"), S.base64().encodeToString(key)); } } protected byte[] createEncryptKey(HttpServletRequest req) { return UUIDSeed.seq().substring(4).getBytes(); } protected String createSessionId(HttpServletRequest req) { return UUIDSeed.random(); } @Override public void init(ServletConfig config) { session = WebSessions.loadUserSession(); } protected UserSession userSession() { return session; } /** * 用于处理登陆业务 * * @param sessionId http头部sid的信息 * @param userName 用户名,默认通过request.getParameter("username")获取的 * @param req 用户请求的HttpServletRequest对象 * @return 登录信息,无论成功与否,返回值不能是null * @throws Exception 异常与failed等价 */ protected abstract LoginObject login(String sessionId, String userName, HttpServletRequest req) throws Exception; } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/AbstractUserSession.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.slf4j.Logger; import org.yx.common.util.S; import org.yx.http.kit.HttpSettings; import org.yx.log.Log; import org.yx.util.StringUtil; import org.yx.util.SumkDate; public abstract class AbstractUserSession implements UserSession { protected final Logger log = Log.get("sumk.http.session"); protected final ConcurrentMap cache = new ConcurrentHashMap<>(); protected abstract TimedCachedObject loadTimedCachedObject(String sessionId, boolean needRefresh); public abstract String getSessionIdByUserFlag(String userId); @Override public byte[] getEncryptKey(String sid) { TimedCachedObject obj = this.loadTimedCachedObject(sid, false); if (obj == null) { return null; } return obj.getKey(); } @Override public T getUserObject(String sessionId, Class clz) { return this._getUserObject(sessionId, clz, false); } @Override public T loadAndRefresh(String sessionId, Class clz) { return this._getUserObject(sessionId, clz, true); } private T _getUserObject(String sessionId, Class clz, boolean refreshTTL) { TimedCachedObject obj = this.loadTimedCachedObject(sessionId, refreshTTL); if (obj == null) { return null; } return S.json().fromJson(obj.json, clz); } @Override public String sessionId(String userId) { if (!HttpSettings.isSingleLogin()) { log.warn("只有设置了sumk.http.session.single=1,本方法才有意义"); return null; } if (StringUtil.isEmpty(userId)) { return null; } String sessionId = this.getSessionIdByUserFlag(userId); if (sessionId == null) { return null; } SessionObject obj = this.getUserObjectBySessionId(sessionId); if (obj == null) { return null; } if (obj.getExpiredTime() == null) { return null; } if (obj.getExpiredTime().longValue() < System.currentTimeMillis()) { log.debug("该过期session的截止时间:{}", SumkDate.of(obj.getExpiredTime())); return null; } return obj.getUserId(); } protected SessionObject getUserObjectBySessionId(String sessionId) { TimedCachedObject obj = this.loadTimedCachedObject(sessionId, false); if (obj == null) { return null; } return S.json().fromJson(obj.json, SessionObject.class); } @Override public int localCacheSize() { return this.cache.size(); } public Map localCache() { return Collections.unmodifiableMap(cache); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/CacheHelper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.yx.log.Log; public final class CacheHelper { public static void expire(Map map, long duration) { Logger log = Log.get("sumk.http.session"); try { expire0(map, duration, log); } catch (Exception e) { log.error(e.getMessage(), e); } } private static void expire0(Map map, long duration, Logger log) { int beginSize = map.size(); long begin = System.currentTimeMillis() - duration; Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { Entry en = it.next(); if (en == null) { continue; } TimedCachedObject t = en.getValue(); if (t == null) { continue; } if (t.refreshTime < begin) { log.trace("{} remove from cache", en.getKey()); it.remove(); } } if (log.isTraceEnabled()) { log.trace("catch size from {} to {},duration:{}", beginSize, map.size(), duration); } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/HttpLoginWrapper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.List; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.bean.IOC; import org.yx.http.server.AbstractCommonHttpServlet; import org.yx.log.Logs; public class HttpLoginWrapper extends AbstractCommonHttpServlet { private static final long serialVersionUID = 1L; private LoginServlet serv; protected void handle(HttpServletRequest req, HttpServletResponse resp) { serv.service(req, resp); } @Override public void init(ServletConfig config) throws ServletException { super.init(config); try { List ss = IOC.getBeans(LoginServlet.class); if (ss == null || ss.isEmpty()) { Logs.http().info("there is no LoginServlet"); return; } if (ss.size() > 1) { Logs.http().warn("there is {} login servlet", ss.size()); } this.serv = ss.get(0); serv.init(config); } catch (Exception e) { Logs.http().error(e.getLocalizedMessage(), e); } } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/HttpSessionFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; public interface HttpSessionFactory { UserSession create(); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/LocalUserSession.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.http.kit.HttpSettings; import org.yx.log.Logs; import org.yx.util.Task; public class LocalUserSession extends AbstractUserSession { protected Map userSessionMap = Collections .synchronizedMap(new LinkedHashMap(128, 0.75f, true) { private static final long serialVersionUID = 1L; @Override protected boolean removeEldestEntry(Entry eldest) { return this.size() > AppInfo.getInt("sumk.http.localsession.maxSize", 1000); } }); public LocalUserSession() { log.info("$$$use local user session"); long seconds = AppInfo.getLong("sumk.http.session.period", 30L); Task.scheduleAtFixedRate(() -> CacheHelper.expire(cache, HttpSettings.httpSessionTimeoutInMs()), seconds, seconds, TimeUnit.SECONDS); } @Override public boolean setSession(String sessionId, SessionObject sessionObj, byte[] key, boolean singleLogin) { TimedCachedObject to = new TimedCachedObject(S.json().toJson(sessionObj), key); to.refreshTime = System.currentTimeMillis(); if (cache.putIfAbsent(sessionId, to) != null) { return false; } if (!singleLogin) { return true; } String oldSessionId = userSessionMap.put(sessionObj.getUserId(), sessionId); if (oldSessionId != null && !oldSessionId.equals(sessionId)) { cache.remove(oldSessionId); } return true; } protected TimedCachedObject loadTimedCachedObject(String sid, boolean needRefresh) { if (sid == null) { return null; } TimedCachedObject to = cache.get(sid); if (to == null) { return null; } long now = System.currentTimeMillis(); if (to.refreshTime + HttpSettings.httpSessionTimeoutInMs() < now) { Logs.http().debug("session:{}实际上已经过期了", sid); cache.remove(sid); return null; } if (needRefresh) { to.refreshTime = now; } return to; } @Override public void removeSession(String sessionId) { if (sessionId == null) { return; } TimedCachedObject to = cache.remove(sessionId); if (to == null || userSessionMap.isEmpty()) { return; } SessionObject obj = S.json().fromJson(to.json, SessionObject.class); this.userSessionMap.remove(obj.userId); } @Override public String getSessionIdByUserFlag(String userId) { return this.userSessionMap.get(userId); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/LoginObject.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.Objects; public class LoginObject { private final String errorMsg; private final String responseData; private final SessionObject sessionObj; public SessionObject getSessionObject() { return sessionObj; } public LoginObject(String responseData, SessionObject sessionObj, String errorMsg) { this.responseData = responseData; this.sessionObj = sessionObj; this.errorMsg = errorMsg; } public String getResponseData() { return responseData; } public String getErrorMsg() { return errorMsg; } public static LoginObject fail(String errorMsg) { return new LoginObject(null, null, errorMsg); } public static LoginObject success(String responseData, SessionObject sessionObj) { return new LoginObject(responseData, Objects.requireNonNull(sessionObj, "sessionObject cannot be null when login successed"), null); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/LoginServlet.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.yx.base.Ordered; public interface LoginServlet extends Ordered { void init(ServletConfig config); void service(HttpServletRequest req, HttpServletResponse resp); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/RemoteUserSession.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.concurrent.TimeUnit; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.http.kit.HttpSettings; import org.yx.redis.Redis; import org.yx.util.StringUtil; import org.yx.util.Task; public class RemoteUserSession extends AbstractUserSession { private static final byte[] NX = { 'N', 'X' }; private static final byte[] PX = { 'P', 'X' }; protected long noFreshTime = AppInfo.getLong("sumk.http.session.cache.nofreshtime", 1000L * 2); protected final int maxSize; protected final Redis redis; public RemoteUserSession(Redis redis) { this.redis = redis.isMuted() ? redis : redis.mute(); this.maxSize = Math.max(AppInfo.getInt("sumk.http.session.cache.maxsize", 5000), 10); long seconds = AppInfo.getLong("sumk.http.session.period", 30L); Task.scheduleAtFixedRate(() -> { long duration = AppInfo.getLong("sumk.http.session.remote.duration", 1000L * 60); if (duration > HttpSettings.httpSessionTimeoutInMs()) { duration = HttpSettings.httpSessionTimeoutInMs(); } CacheHelper.expire(cache, duration); }, seconds, seconds, TimeUnit.SECONDS); } protected final String singleKey(String userId) { return "_SINGLE_SES_".concat(userId); } protected final byte[] redisSessionKey(String sessionId) { if (sessionId == null || sessionId.isEmpty()) { return null; } return ("_SES_#" + sessionId).getBytes(AppInfo.UTF8); } private void removeOne() { try { String key = cache.keySet().iterator().next(); log.debug("remove session {} from local cache", key); cache.remove(key); } catch (Exception e) { log.error(e.getMessage(), e); } } @Override protected TimedCachedObject loadTimedCachedObject(String sid, boolean needRefresh) { if (sid == null) { return null; } byte[] bigKey = this.redisSessionKey(sid); TimedCachedObject to = cache.get(sid); long now = System.currentTimeMillis(); if (to == null) { byte[] bv = redis.get(bigKey); to = TimedCachedObject.deserialize(bv); if (to == null) { if (log.isTraceEnabled()) { log.trace("{} cannot found from redis", sid); } return null; } if (log.isTraceEnabled()) { log.trace("{} add to local cache", sid); } if (cache.size() >= this.maxSize) { removeOne(); } cache.put(sid, to); } if (needRefresh && to.refreshTime + this.noFreshTime < now) { long durationInMS = HttpSettings.httpSessionTimeoutInMs(); Long v = redis.pexpire(bigKey, durationInMS); if (v != null && v.longValue() == 0) { cache.remove(sid); log.trace("{} was pexpire by redis,and remove from local cache", sid); return null; } if (log.isTraceEnabled()) { log.trace("{} was refresh in redis", sid); } to.refreshTime = now; } return to; } @Override public void removeSession(String sessionId) { byte[] bigKey = this.redisSessionKey(sessionId); if (bigKey == null) { return; } if (HttpSettings.isSingleLogin()) { SessionObject t = getUserObjectBySessionId(sessionId); if (t != null) { redis.del(this.singleKey(t.getUserId())); } } this.cache.remove(sessionId); redis.del(bigKey); } @Override public boolean setSession(String sessionId, SessionObject sessionObj, byte[] key, boolean singleLogin) { long sessionTimeout = HttpSettings.httpSessionTimeoutInMs(); byte[] bigKey = this.redisSessionKey(sessionId); String json = S.json().toJson(sessionObj); byte[] data = TimedCachedObject.toBytes(json, key); String ret = redis.set(bigKey, data, NX, PX, sessionTimeout); if (!"OK".equalsIgnoreCase(ret) && !"1".equals(ret)) { return false; } if (!singleLogin) { return true; } String singleUserKey = singleKey(sessionObj.userId); String oldSessionId = redis.get(singleUserKey); if (StringUtil.isNotEmpty(oldSessionId) && !oldSessionId.equals(sessionId)) { redis.del(redisSessionKey(oldSessionId)); cache.remove(oldSessionId); } long expireTime = sessionObj.getExpiredTime() != null ? sessionObj.getExpiredTime() - System.currentTimeMillis() : -1; if (expireTime < 1) { expireTime = AppInfo.getLong("sumk.http.session.single.maxTime", 1000L * 3600 * 20); } redis.psetex(singleUserKey, expireTime, sessionId); return true; } @Override public String getSessionIdByUserFlag(String userId) { return redis.get(this.singleKey(userId)); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/SessionObject.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; /** * 用户session,要有无构造参数的构造函数。 */ public class SessionObject { protected String userId; private Long expiredTime; /** * 返回用户id * * @return 用户id,不能为null */ public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public Long getExpiredTime() { return expiredTime; } /** * 如果设置了过期时间,即使用户一直在操作, 当达到过期时间后,session也会被清理 * * @param expiredTime 最大的过期时间,精确到毫秒。null表示不自动清理 */ public void setExpiredTime(Long expiredTime) { this.expiredTime = expiredTime; } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/TimedCachedObject.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.Arrays; import java.util.Objects; import org.yx.conf.AppInfo; public class TimedCachedObject { long refreshTime; final String json; final byte[] key; public TimedCachedObject(String json, byte[] key) { this.json = Objects.requireNonNull(json); this.key = Objects.requireNonNull(key); } public long getRefreshTime() { return refreshTime; } public String getJson() { return json; } public byte[] getKey() { return key; } public void setRefreshTime(long refreshTime) { this.refreshTime = refreshTime; } public final boolean isExpired(long duration, long now) { return this.refreshTime + duration < now; } public static byte[] toBytes(String json, byte[] key) { int keyLength = key.length; int b1 = keyLength & 0xFF; int b0 = (keyLength >> 8) & 0xFF; byte[] j = json.getBytes(AppInfo.UTF8); byte[] ret = new byte[2 + keyLength + j.length]; ret[0] = (byte) b0; ret[1] = (byte) b1; System.arraycopy(key, 0, ret, 2, keyLength); System.arraycopy(j, 0, ret, 2 + keyLength, j.length); return ret; } public static TimedCachedObject deserialize(byte[] bv) { if (bv == null || bv.length < 3) { return null; } int keyLength = (bv[0] & 0xff) << 8 | (bv[1] & 0xff); String json = new String(bv, 2 + keyLength, bv.length - 2 - keyLength, AppInfo.UTF8); return new TimedCachedObject(json, Arrays.copyOfRange(bv, 2, 2 + keyLength)); } } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/UserSession.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; public interface UserSession { byte[] getEncryptKey(String sessionId); T getUserObject(String sessionId, Class clz); T loadAndRefresh(String sessionId, Class clz); boolean setSession(String sessionId, SessionObject sessionObj, byte[] key, boolean singleLogin); void removeSession(String sessionId); String sessionId(String userId); int localCacheSize(); } ================================================ FILE: sumk-http/src/main/java/org/yx/http/user/WebSessions.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.http.user; import java.util.Objects; import java.util.function.Predicate; import org.yx.bean.IOC; import org.yx.exception.BizException; import org.yx.http.HttpErrorCode; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.redis.Redis; import org.yx.redis.RedisLoader; import org.yx.redis.RedisPool; public final class WebSessions { private static UserSession session; private static Predicate sessionIdVerifier = s -> s != null && s.length() > 10; public static UserSession userSession() { return session; } public static UserSession loadUserSession() { if (session == null) { Log.get("sumk.http.session").info("session has not created"); throw BizException.create(HttpErrorCode.SESSION_ERROR, "请重新登录"); } return session; } public static Predicate getSessionIdVerifier() { return sessionIdVerifier; } public static void setSessionIdVerifier(Predicate sessionIdVerifier) { WebSessions.sessionIdVerifier = Objects.requireNonNull(sessionIdVerifier); } public static T getUserObject(String sessionId, Class clz) { return loadUserSession().getUserObject(sessionId, clz); } public static void remove(String sessionId) { userSession(); if (session == null) { Log.get("sumk.http.session").debug("has removed"); return; } session.removeSession(sessionId); } public static synchronized void initSession() { if (session != null) { return; } HttpSessionFactory factory = IOC.get(HttpSessionFactory.class); session = factory != null ? factory.create() : createDefaultUserSession(); } private static UserSession createDefaultUserSession() { try { Redis redis = RedisPool.getRedisExactly(RedisLoader.SESSION); return redis == null ? new LocalUserSession() : new RemoteUserSession(redis); } catch (NoClassDefFoundError e) { Logs.http().debug("use local session because redis cannot load", e); return new LocalUserSession(); } } } ================================================ FILE: sumk-http/src/main/resources/META-INF/http/listeners ================================================ javax.servlet.ServletContextAttributeListener javax.servlet.ServletRequestListener javax.servlet.ServletRequestAttributeListener javax.servlet.http.HttpSessionListener javax.servlet.http.HttpSessionAttributeListener ================================================ FILE: sumk-http/src/main/resources/META-INF/sumk.factories ================================================ sumk.ioc.bean=org.yx.http.handler.Base64DecodeHandler,org.yx.http.handler.Base64EncodeHandler,\ org.yx.http.handler.DecryptHandler,org.yx.http.handler.EncryptHandler,\ org.yx.http.handler.InvokeHandler,org.yx.http.handler.MultipartHandler,\ org.yx.http.handler.ReqDataHandler,org.yx.http.handler.ReqSignValidateHandler,\ org.yx.http.handler.ReqToStringHandler,org.yx.http.handler.ReqUserHandler,\ org.yx.http.handler.RespBodyHandler,org.yx.http.handler.RespToStringHandler,\ org.yx.http.handler.ToBytesHandler,\ org.yx.http.server.DocumentServlet,org.yx.http.server.MultipartServer,\ org.yx.http.server.RestServer,org.yx.http.server.SumkMonitor,\ org.yx.http.HttpPlugin sumk.ioc.optional=org.yx.http.* ================================================ FILE: sumk-rpc/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright youtongluan (\u6e38\u901a\u92ae\uff0c\u522b\u540d\uff1a\u6e38\u590f) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: sumk-rpc/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-rpc com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots com.github.youtongluan sumk-framework ${project.version} org.apache.zookeeper zookeeper io.netty netty-handler org.slf4j slf4j-log4j12 io.netty netty-transport-native-epoll log4j log4j ch.qos.logback logback-core ch.qos.logback logback-classic io.netty netty-handler com.101tec zkclient netty io.netty log4j log4j slf4j-log4j12 org.slf4j junit junit test ================================================ FILE: sumk-rpc/rpc.md ================================================ ## 客户端 ### 建立连接 1. Rpc.init()初始化Transport 2. 请求某个地址的时候,视情况创建TransportClient 3. TransportClient创建TransportChannel而TransportChannel实例创建之前,会创建mina的session或netty的channel。并且将setAttribute(Client.getName(),Client),setAttribute(TransportChannel.getName(),channel)。这样session就跟channel和client关联起来 ### 断开连接 * 外部通过TransportClient来关闭。 * ClientHandler中只是通过Channel来关闭就行了。因为框架有定时监测idle的功能,所以被意外关闭的无需在意,让定时器来监测就好 * 各个实现类要设法监听inputClose方法,它去close session ## 服务端 ### 接收连接 1. SoaPlugin初始化Transport 2.Transport初始化TransportServer 2. Handler第一次接收session数据的时候,创建TransportChannel,并且setAttribute(TransportChannel.getName(),channel) ### 断开连接 * server只有idle、异常会主动断开连接 * 各个实现类要设法监听inputClose方法,它去close session ================================================ FILE: sumk-rpc/src/main/java/org/yx/annotation/rpc/Soa.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.rpc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Soa { /** * 在@SoaClass注解中,这个参数会被忽略 * * @return 服务名称,如果为空,就使用method name。 */ String value() default ""; String cnName() default ""; /** * 发布的服务名称是否加上appId前缀,只在appId不为空的时候才有作用 * * @return appId前缀 */ boolean appIdPrefix() default true; int toplimit() default 0; /** * 可以通过sumk.rpc.publish.[api名称]配置来覆盖本属性。
* 设置sumk.rpc.server.register=0可以将整个应用的zk注册给禁用掉 * * @return 是否将接口发布到zk上 */ boolean publish() default true; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/annotation/rpc/SoaClass.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.rpc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SoaClass { /** * 发布使用微服务使用的类型,一般是它继承的接口。 如果该类没有实现接口,或者只有一个接口,可以使用默认的Void * * @return 当前类、它实现的接口、它继承的超类 */ Class refer() default Void.class; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/annotation/rpc/SoaClientConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.annotation.rpc; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 仅对使用接口调用的rpc客户端有作用,注解作用在接口上面。 它用于定义额外的soa参数 */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SoaClientConfig { int timeout() default -1; int tryCount() default -1; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/BusinessHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc; import org.yx.rpc.transport.TransportChannel; public interface BusinessHandler { void received(TransportChannel channel, Object message); void exceptionCaught(TransportChannel channel, Throwable exception); void closed(TransportChannel channel); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/Profile.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.yx.rpc.codec.Protocols; public final class Profile { public static final Charset UTF8 = StandardCharsets.UTF_8; public static final int version = 0x160; public static long feature() { long v = version; v <<= 32; v |= Protocols.profile() & 0xFFFFFFFFL; return v; } public static String featureInHex() { return Long.toHexString(feature()); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/RpcErrorCode.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc; public interface RpcErrorCode { /** * 线程数溢出 */ String THREAD_THRESHOLD_OVER = "700"; String WAIT_TWICE = "710"; /** * 该api没有配置路由信息 */ String NO_ROUTE = "720"; /** * 存在节点,但是节点不能用 */ String NO_NODE_AVAILABLE = "730"; /** * 客户端等待超时 */ String TIMEOUT = "740"; /** * 客户端发送失败,这个code会触发失败重试。 如果开启了失败重试,客户端一般不会得到这个异常码,而是NO_NODE_AVAILABLE */ String SEND_FAILED = "750"; /** * 客户端参数类型错误、服务器端业务代码出错等,并且异常类型不是BizException */ String SERVER_HANDLE_ERROR = "760"; /** * 服务器端未知出错 */ String SERVER_UNKNOW = "770"; /** * 没有合适的handler */ String NO_MAPPED_UNKNOW = "777"; /** * 客户端的未知错误 */ String UNKNOW = "799"; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/RpcJson.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc; import java.util.Objects; import org.yx.annotation.ExcludeFromParams; import org.yx.common.json.GsonHelper; import org.yx.common.json.GsonOperator; import org.yx.common.json.JsonOperator; import org.yx.common.json.ServerJsonExclusionStrategy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public final class RpcJson { private static GsonBuilder gsonBuilder() { return GsonHelper.builder("sumk.rpc"); } private static Gson createServerGson() { return ServerJsonExclusionStrategy.addServerExclusionStrategy(gsonBuilder()).create(); } private static Gson createClientGson() { return gsonBuilder().addSerializationExclusionStrategy(new ServerJsonExclusionStrategy(ExcludeFromParams.class)) .create(); } private static JsonOperator server = new GsonOperator(createServerGson()); private static JsonOperator client = new GsonOperator(createClientGson()); private static JsonOperator operator = new GsonOperator(gsonBuilder().create()); public static JsonOperator operator() { return operator; } public static void setOperator(JsonOperator operator) { RpcJson.operator = Objects.requireNonNull(operator); } public static JsonOperator server() { return server; } public static void setServerOperator(JsonOperator operator) { RpcJson.server = Objects.requireNonNull(operator); } public static JsonOperator client() { return client; } public static void setClientOperator(JsonOperator operator) { RpcJson.client = Objects.requireNonNull(operator); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/RpcSettings.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc; import org.yx.conf.AppInfo; public final class RpcSettings { private static boolean started; private static boolean serverLogDisable; private static boolean clientLogDisable; private static long infoTime; private static long warnTime; private static int clientDefaultTimeout; private static boolean disableLocalRoute; private static int clientTryCount; private static boolean showServerExceptionLog; private static int maxReqLogSize; private static int maxRespLogSize; public static int maxReqLogSize() { return maxReqLogSize; } public static int maxRespLogSize() { return maxRespLogSize; } public static boolean showServerExceptionLog() { return showServerExceptionLog; } public static boolean disableLocalRoute() { return disableLocalRoute; } public static int clientDefaultTimeout() { return clientDefaultTimeout; } public static long infoTime() { return infoTime; } public static long warnTime() { return warnTime; } public static boolean isServerLogDisable() { return serverLogDisable; } public static int clientTryCount() { return clientTryCount; } public static boolean isClientLogDisable() { return clientLogDisable; } public static long maxServerIdleTime() { return AppInfo.getLong("sumk.rpc.server.idle", 1000L * 60 * 10); } public static long maxClientIdleTime() { return AppInfo.getLong("sumk.rpc.client.idle", 1000L * 60 * 5); } public synchronized static void init() { if (started) { return; } started = true; AppInfo.addObserver(info -> { RpcSettings.warnTime = AppInfo.getInt("sumk.rpc.log.warn.time", 3000); RpcSettings.infoTime = AppInfo.getInt("sumk.rpc.log.info.time", 1000); RpcSettings.serverLogDisable = AppInfo.getBoolean("sumk.rpc.log.server.disable", false); RpcSettings.clientLogDisable = AppInfo.getBoolean("sumk.rpc.log.client.disable", false); RpcSettings.clientDefaultTimeout = AppInfo.getInt("sumk.rpc.call.timeout", 30000); RpcSettings.clientTryCount = AppInfo.getInt("sumk.rpc.client.trycount", 3); RpcSettings.disableLocalRoute = AppInfo.getBoolean("sumk.rpc.localroute.disable", false); RpcSettings.showServerExceptionLog = AppInfo.getBoolean("sumk.rpc.server.exceptionlog", false); RpcSettings.maxReqLogSize = AppInfo.getInt("sumk.rpc.log.reqsize", 1000); RpcSettings.maxRespLogSize = AppInfo.getInt("sumk.rpc.log.respsize", 5000); }); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/RpcUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc; import java.util.Collections; import java.util.List; import java.util.Map; import org.yx.common.Host; import org.yx.base.context.ActionContext; import org.yx.common.route.Router; import org.yx.rpc.registry.client.RpcRoutes; import org.yx.rpc.server.LocalRpcContext; import org.yx.rpc.server.RpcContext; public final class RpcUtil { public static String userId() { return ActionContext.current().userId(); } public static void setUserId(String userId) { ActionContext.current().userId(userId); } public static boolean setUserIdIfEmpty(String userId) { ActionContext tc = ActionContext.current(); if (tc.userId() == null) { tc.userId(userId); return true; } return false; } public static Map attachmentView() { return ActionContext.current().attachmentView(); } public static void setAttachment(String key, String value) { ActionContext.current().setAttachment(key, value); } public static String getAttachment(String key) { return ActionContext.current().getAttachment(key); } public static void removeContext() { ActionContext.remove(); } public static List allServers(String api) { Router route = RpcRoutes.getRoute(api); if (route == null) { return Collections.emptyList(); } return route.allSources(); } public static List aliveServers(String api) { Router route = RpcRoutes.getRoute(api); if (route == null) { return Collections.emptyList(); } return route.aliveSources(); } public static RpcContext context() { return LocalRpcContext.getCtx(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/AbstractRpcFuture.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.Objects; import org.yx.base.context.LogContext; import org.yx.common.Host; import org.yx.common.util.S; import org.yx.exception.CodeException; public abstract class AbstractRpcFuture implements RpcFuture { protected final RpcLocker locker; public AbstractRpcFuture(RpcLocker locker) { this.locker = Objects.requireNonNull(locker); } @Override public T getOrException(Class clz) throws CodeException { String json = this.getOrException(); if (json == null) { return null; } return S.json().fromJson(json, clz); } @Override public String getOrException() throws CodeException { RpcResult resp = this.awaitForRpcResult(); resp.throwIfException(); return resp.json(); } @Override public T opt(Class clz) throws CodeException { String json = this.opt(); if (json == null) { return null; } return S.json().fromJson(json, clz); } @Override public String opt() throws CodeException { RpcResult resp = this.awaitForRpcResult(); return resp.json(); } @Override public Host getServer() { return this.locker.url(); } public LogContext getOriginLogContext() { return this.locker.originLogContext(); } @Override public String getRequestId() { return this.locker.req.getSn(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/AbstractTransportClient.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.Objects; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.yx.common.Host; import org.yx.log.Logs; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.TransportChannel; import org.yx.rpc.transport.TransportClient; public abstract class AbstractTransportClient implements TransportClient { protected volatile TransportChannel channel; protected final Host addr; protected final Lock lock = new ReentrantLock(); public AbstractTransportClient(Host host) { this.addr = Objects.requireNonNull(host); } private boolean ensureSession() { if (channel != null && !channel.isClosing()) { return true; } try { connect(); } catch (Exception e1) { Logs.rpc().error(this.addr + " - " + e1.toString(), e1); } if (channel == null || channel.isClosing()) { HostChecker.get().addDownUrl(addr); return false; } return true; } protected abstract void connect() throws Exception; @Override public RpcWriteFuture write(Req req) { if (!this.ensureSession()) { return null; } return this.channel.write(req); } @Override public void closeIfPossibble() { TransportChannel s = null; if (lock.tryLock()) { try { s = this.channel; this.channel = null; } finally { lock.unlock(); } } else { Logs.rpc().warn("关闭rpc连接时获取锁失败-{}", this); return; } if (s != null && s.isConnected()) { s.closeOnFlush(); } } @Override public boolean isIdle() { TransportChannel s = this.channel; return s == null || s.isClosing(); } @Override public final Host getRemoteAddr() { return this.addr; } @Override public final TransportChannel getChannel() { return this.channel; } @Override public String toString() { return this.getClass().getName() + " [addr=" + addr + ", session=" + channel + "]"; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/Client.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import org.yx.common.Host; import org.yx.base.context.ActionContext; import org.yx.common.route.Router; import org.yx.exception.SoaException; import org.yx.log.Logs; import org.yx.rpc.RpcErrorCode; import org.yx.rpc.RpcJson; import org.yx.rpc.RpcSettings; import org.yx.rpc.codec.Request; import org.yx.rpc.context.InnerRpcUtil; import org.yx.rpc.context.RpcActionNode; import org.yx.rpc.context.RpcActions; import org.yx.rpc.registry.client.RpcRoutes; import org.yx.rpc.server.LocalRequestHandler; import org.yx.rpc.server.Response; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.TransportClient; import org.yx.util.UUIDSeed; public final class Client { private static final Host LOCAL = Host.create("local", 0); private static final AtomicInteger COUNTER = new AtomicInteger(); private final String api; private Object params; private ParamType paramType; private int totalTimeout; private Host[] directUrls; private boolean backup; private int tryCount; private Consumer callback; public Client(String api) { this.api = Objects.requireNonNull(api).trim(); this.totalTimeout = RpcSettings.clientDefaultTimeout(); this.tryCount = RpcSettings.clientTryCount(); } public Client directUrls(Host... urls) { this.directUrls = urls; return this; } /** * 设置发送的尝试次数。只有发送失败会重试,其它的不会 * * @param tryCount 尝试次数,包含第一次发送 * @return 当前对象 */ public Client tryCount(int tryCount) { this.tryCount = tryCount > 0 ? tryCount : 1; return this; } /** * 设置直连url不能用的时候,是否使用注册中心上的地址 * * @param backup 失败时是否启用注册中心上的地址 * @return 当前对象 */ public Client backup(boolean backup) { this.backup = backup; return this; } public Client timeout(int timeout) { this.totalTimeout = timeout > 0 ? timeout : 1; return this; } public Client callback(Consumer callback) { this.callback = callback; return this; } public Client paramInArray(Object... args) { if (args == null) { args = new String[0]; } String[] params = new String[args.length]; for (int i = 0; i < args.length; i++) { Object arg = args[i]; if (arg == null) { params[i] = null; } else if (arg.getClass() == String.class) { params[i] = (String) arg; } else { params[i] = RpcJson.client().toJson(arg); } } this.params = params; this.paramType = ParamType.JSONARRAY; return this; } public Client paramInJson(String json) { this.params = json; this.paramType = ParamType.JSON; return this; } public Client paramInMap(Map map) { return paramInJson(RpcJson.client().toJson(map)); } protected Req createReq() { Req req = new Req(); ActionContext context = ActionContext.current(); if (context.isTest()) { req.setTest(true); } req.setStart(System.currentTimeMillis()); String sn = UUIDSeed.seq18(); req.setFullSn(sn, context.traceId(), context.nextSpanId()); req.setUserId(context.userId()); req.setApi(this.api); req.setFrom(Rpc.appId()); req.setAttachments(context.attachmentView()); return req; } /** * 本方法调用之后,不允许再调用本对象的任何方法
* * @return 用无论是否成功,都会返回future。如果失败的话,异常包含在future中。
* 通信异常是SoaException;如果是业务类异常,则是BizException */ public RpcFuture execute() { Objects.requireNonNull(this.paramType, "param have not been set"); Req req = this.createReq(); long endTime = req.getStart() + this.totalTimeout; req.setParams(this.paramType.protocol(), this.params); int count = this.tryCount; while (true) { RpcFuture f = sendAsync(req, endTime); if (f.getClass() == ErrorRpcFuture.class) { ErrorRpcFuture errorFuture = (ErrorRpcFuture) f; RpcLocker locker = errorFuture.locker; LockHolder.remove(locker.req.getSn()); if (--count > 0 && errorFuture.rpcResult().exception().isSameCode(RpcErrorCode.SEND_FAILED) && System.currentTimeMillis() + 5 < endTime) { locker.discard(errorFuture.rpcResult()); Logs.rpc().warn("无法发送数据到{},重试rpc请求", locker.url()); continue; } locker.wakeupAndLog(errorFuture.rpcResult()); } return f; } } private Host selectDirectUrl() { int index = COUNTER.incrementAndGet(); if (index < 0) { COUNTER.set((int) (System.nanoTime() & 0xff)); index = COUNTER.incrementAndGet(); } for (int i = 0; i < this.directUrls.length; i++) { index %= directUrls.length; Host url = this.directUrls[index]; if (!HostChecker.get().isDowned(url)) { return url; } } return null; } private RpcFuture sendAsync(final Req req, final long endTime) { final RpcLocker locker = new RpcLocker(req, callback); Host url = null; if (this.directUrls != null && this.directUrls.length > 0) { url = selectDirectUrl(); if (url == null && !this.backup) { SoaException ex = new SoaException(RpcErrorCode.NO_NODE_AVAILABLE, "all directUrls is disabled:" + Arrays.toString(this.directUrls), null); return new ErrorRpcFuture(ex, locker); } } if (url == null) { Router route = RpcRoutes.getRoute(api); RpcFuture future = this.tryLocalHandler(req, locker, route); if (future != null) { return future; } if (route == null) { SoaException ex = new SoaException(RpcErrorCode.NO_ROUTE, "can not find route for " + api, null); return new ErrorRpcFuture(ex, locker); } url = route.select(); } if (url == null) { SoaException ex = new SoaException(RpcErrorCode.NO_NODE_AVAILABLE, "route for " + api + " are all disabled", null); return new ErrorRpcFuture(ex, locker); } locker.url(url); req.setServerProtocol(RpcRoutes.getServerProtocol(url)); RpcWriteFuture f = null; try { TransportClient reqSession = TransportClientHolder.getSession(url); LockHolder.register(locker, endTime); f = reqSession.write(req); } catch (Exception e) { Logs.rpc().error(e.getLocalizedMessage(), e); } if (f == null) { SoaException ex = new SoaException(RpcErrorCode.SEND_FAILED, url + " can not connect", null); return new ErrorRpcFuture(ex, locker); } f.addListener(locker); return new RpcFutureImpl(locker); } private RpcFuture tryLocalHandler(Req req, RpcLocker locker, Router route) { RpcActionNode node = RpcActions.getActionNode(api); if (node == null) { return null; } if (RpcSettings.disableLocalRoute() && route != null) { return null; } Request request = new Request(req); req = null; ActionContext context = ActionContext.current().clone(); try { InnerRpcUtil.rpcContext(request, context.isTest()); locker.url(LOCAL); Response resp = LocalRequestHandler.inst.handler(request, node); ActionContext.store(context); locker.wakeupAndLog(new RpcResult(resp.json(), resp.exception())); } finally { ActionContext.store(context); } return new RpcFutureImpl(locker); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/ClientHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import org.yx.log.Log; import org.yx.rpc.BusinessHandler; import org.yx.rpc.server.Response; import org.yx.rpc.transport.TransportChannel; public class ClientHandler implements BusinessHandler { @Override public void received(TransportChannel channel, Object message) { if (message == null) { return; } if (message instanceof Response) { Response resp = (Response) message; LockHolder.unLockAndSetResult(resp); return; } Log.get("sumk.rpc.client").warn("unkown client message type:{}", message.getClass().getName()); } @Override public void exceptionCaught(TransportChannel channel, Throwable exception) { Log.get("sumk.rpc.client").error(channel + " throw exception", exception); channel.closeNow(); } @Override public void closed(TransportChannel channel) { } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/ErrorRpcFuture.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import org.yx.exception.CodeException; import org.yx.exception.SoaException; import org.yx.rpc.RpcErrorCode; public class ErrorRpcFuture extends AbstractRpcFuture { private final RpcResult rpcResult; public ErrorRpcFuture(Throwable e, RpcLocker locker) { super(locker); CodeException exception = e instanceof CodeException ? (CodeException) e : SoaException.create(RpcErrorCode.UNKNOW, e.getMessage(), e); this.rpcResult = new RpcResult(null, exception); } public RpcResult awaitForRpcResult() { return this.rpcResult; } @Override public RpcResult rpcResult() { return this.rpcResult; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/HostChecker.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.util.Task; public class HostChecker { private static final HostChecker holder = new HostChecker(); private HostChecker() { Task.scheduleAtFixedRate(new Checker(), 5, AppInfo.getInt("sumk.rpc.hosts.check.period", 3), TimeUnit.SECONDS); } public static HostChecker get() { return holder; } private final ConcurrentHashMap downUrls = new ConcurrentHashMap<>(); public boolean isDowned(Host url) { return downUrls.containsKey(url); } public List available(List urls) { List us = new ArrayList<>(urls); List avas = new ArrayList<>(us.size()); for (Host u : us) { if (!downUrls.containsKey(u)) { avas.add(u); } } return avas; } public void addDownUrl(Host url) { if (url == null) { Logs.rpc().warn("url is null"); return; } downUrls.putIfAbsent(url, System.currentTimeMillis()); Logs.rpc().info("{} is down", url); } private class Checker implements Runnable { private int getTimeOut(int urlSize) { if (urlSize == 1) { return AppInfo.getInt("sumk.rpc.socket.connecttimeout.1", 3000); } else if (urlSize == 2) { return AppInfo.getInt("sumk.rpc.socket.connecttimeout.2", 2500); } else if (urlSize > 5) { return AppInfo.getInt("sumk.rpc.socket.connecttimeout.5", 1000); } return AppInfo.getInt("sumk.rpc.socket.connecttimeout.3", 2000); } @Override public void run() { if (downUrls.isEmpty()) { return; } Host[] urls = downUrls.keySet().toArray(new Host[0]); int timeout = getTimeOut(urls.length); long maxDownMilSecond = AppInfo.getLong("sumk.rpc.returnToAlive", 1000L * 60 * 10); long now = System.currentTimeMillis(); for (Host url : urls) { Long addTime = downUrls.get(url); if (addTime != null) { long passedTime = now - addTime; if (passedTime > maxDownMilSecond) { downUrls.remove(url); Logs.rpc().debug("{} remove from checker because it has been retried for {}ms", url, passedTime); continue; } } try (Socket socket = new Socket()) { socket.connect(url.toInetSocketAddress(), timeout); if (socket.isConnected()) { downUrls.remove(url); Logs.rpc().info("{} reconected", url); } } catch (UnknownHostException e) { Logs.rpc().error(e.getMessage(), e); } catch (Exception e) { } } } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/LockHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.rpc.server.Response; import org.yx.util.Task; public final class LockHolder { private static final ConcurrentMap locks = new ConcurrentHashMap<>(); static final LockTimeoutMonitor monitor = new LockTimeoutMonitor(); static { Task.scheduleAtFixedRate(monitor, 1000, 500); } static void register(RpcLocker r, long endTime) { Req req = r.req; if (locks.putIfAbsent(req.getSn(), r) != null) { throw new SumkException(111111111, req.getSn() + " duplicate!!!!!!!!!!!!!!!!!!!!!"); } monitor.add(req.getSn(), endTime); } static void unLockAndSetResult(Response resp) { RpcLocker r = locks.remove(resp.sn()); if (r == null) { Log.get("sumk.rpc.client").debug("{} has been removed.maybe is timeout.result:{}", resp.sn(), resp.json()); return; } RpcResult result = new RpcResult(resp.json(), resp.exception()); r.wakeupAndLog(result); } static RpcLocker remove(String sn) { return locks.remove(sn); } static boolean containsKey(String sn) { return locks.containsKey(sn); } public static int lockSize() { return locks.size(); } private static final class LockTimeoutMonitor implements Runnable { private static final DelayQueue QUEUE = new DelayQueue<>(); @Override public void run() { try { DelayedObject delay; while ((delay = QUEUE.poll()) != null) { RpcLocker locker = LockHolder.remove(delay.sn); if (locker != null) { locker.wakeupAndLog(RpcResult.timeout(locker.req)); } } } catch (Exception e) { Log.printStack("sumk.error", e); return; } } void add(String reqId, long endTime) { QUEUE.add(new DelayedObject(reqId, endTime)); } } private static final class DelayedObject implements Delayed { private final long endTime; final String sn; public DelayedObject(String sn, long endTime) { this.endTime = endTime; this.sn = sn; } @Override public long getDelay(TimeUnit unit) { return unit.convert(endTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed other) { long d = other instanceof DelayedObject ? this.endTime - ((DelayedObject) other).endTime : this.getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS); return (d == 0) ? 0 : ((d < 0) ? -1 : 1); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/ParamType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import org.yx.rpc.codec.ReqParamType; enum ParamType { JSONARRAY(ReqParamType.REQ_PARAM_ORDER), JSON(ReqParamType.REQ_PARAM_JSON); private final int protocol; private ParamType(int p) { this.protocol = p; } public int protocol() { return this.protocol; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/Req.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import org.yx.base.sumk.map.ListMap; import org.yx.conf.Const; import org.yx.rpc.codec.CodecKit; import org.yx.rpc.codec.DataStream; import org.yx.rpc.codec.Protocols; import org.yx.rpc.codec.ReqParamType; import org.yx.rpc.codec.StreamAble; import org.yx.util.CollectionUtil; public class Req implements StreamAble, Serializable { private static final long serialVersionUID = 123L; private String api; private String userId; private String sn; private String spanId; private String traceId; private Object params; private String from; private long startTime; protected int protocol = Const.SUMK_VERSION; private int _serverProtocol; private Map attachments; public void setFullSn(String sn, String traceId, String spanId) { this.sn = Objects.requireNonNull(sn); this.traceId = traceId; this.spanId = Objects.requireNonNull(spanId); } public String getJsonedParam() { return this.hasFeature(ReqParamType.REQ_PARAM_JSON) ? (String) params : null; } public void setParams(int type, Object params) { this.setParamType(type & Protocols.REQUEST_PARAM_TYPES); this.params = params; } private void setParamType(int type) { int p = this.protocol & (~Protocols.REQUEST_PARAM_TYPES); this.protocol = p | type; } public Object getParams() { return this.params; } /** * 数组方式传参才有,否则为null * * @return 如果参数是null,返回的也是null,如果是String类型,就是参数本身,否则是参数的json */ public String[] getParamArray() { return this.hasFeature(ReqParamType.REQ_PARAM_ORDER) ? (String[]) params : null; } public long getStart() { return startTime; } public void setStart(long start) { this.startTime = start; } public String getSn() { return sn; } public String getTraceId() { return this.traceId; } public String getSpanId() { return this.spanId; } public String getApi() { return api; } public void setApi(String api) { this.api = api; } public String getFrom() { return from; } public void setFrom(String src) { this.from = src; } public void addFeature(int feature) { this.protocol |= feature; } public boolean hasFeature(int feature) { return Protocols.hasFeature(protocol, feature); } public int protocol() { return this.protocol; } public void setTest(boolean b) { addFeature(Protocols.TEST); } public boolean isTest() { return hasFeature(Protocols.TEST); } public String getUserId() { return this.userId; } public void setUserId(String userId) { this.userId = userId; } public Map getAttachments() { return attachments; } public void setAttachments(Map attachments) { this.attachments = attachments == null ? null : CollectionUtil.unmodifyMap(attachments); } public int getServerProtocol() { return _serverProtocol; } public void setServerProtocol(int serverProtocol) { this._serverProtocol = serverProtocol; } @Override public void writeTo(DataStream s) throws Exception { s.writeInt(8, 1); s.writePrefixedString(Integer.toString(this.protocol, Character.MAX_RADIX), 1); s.writePrefixedString(this.api, 1); s.writePrefixedString(this.userId, 1); s.writePrefixedString(this.sn, 1); s.writePrefixedString(this.traceId, 1); s.writePrefixedString(this.spanId, 1); s.writePrefixedString(this.from, 1); s.writePrefixedString(Long.toString(this.startTime, Character.MAX_RADIX), 1); int size = this.attachments == null ? 0 : this.attachments.size(); s.writeInt(size, 1); if (size > 0) { for (Entry en : this.attachments.entrySet()) { s.writePrefixedString(en.getKey(), 1); s.writePrefixedString(en.getValue(), 2); } } if (this.hasFeature(ReqParamType.REQ_PARAM_JSON)) { s.writePrefixedString(this.getJsonedParam(), 4); } else { String[] ps = this.getParamArray(); s.writeInt(ps.length, 1); for (String p : ps) { s.writePrefixedString(p, 4); } } } @Override public void readFrom(DataStream s) throws Exception { int size = s.readInt(1); this.protocol = Integer.parseInt(s.readPrefixedString(1), Character.MAX_RADIX); this.api = s.readPrefixedString(1); this.userId = s.readPrefixedString(1); this.sn = s.readPrefixedString(1); this.traceId = s.readPrefixedString(1); this.spanId = s.readPrefixedString(1); this.from = s.readPrefixedString(1); this.startTime = Long.parseLong(s.readPrefixedString(1), Character.MAX_RADIX); CodecKit.skipPrefixedString(s, size - 8); size = s.readInt(1); if (size > 0) { Map map = new ListMap<>(size); for (int i = 0; i < size; i++) { map.put(s.readPrefixedString(1), s.readPrefixedString(2)); } this.attachments = CollectionUtil.unmodifyMap(map); } if (this.hasFeature(ReqParamType.REQ_PARAM_JSON)) { this.params = s.readPrefixedString(4); } else { size = s.readInt(1); String[] ps = new String[size]; for (int i = 0; i < size; i++) { ps[i] = s.readPrefixedString(4); } this.params = ps; } } @Override public int getMessageType() { return Protocols.REQUEST; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/Rpc.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executor; import org.yx.bean.IOC; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.main.SumkServer; import org.yx.rpc.RpcSettings; import org.yx.rpc.registry.RegistryFactory; import org.yx.rpc.registry.client.RegistryClient; import org.yx.rpc.transport.Transports; import org.yx.util.SumkThreadPool; import org.yx.util.Task; public final class Rpc { private Rpc() { } private static volatile boolean strated; private static String appId; private static Optional registryClient; static String appId() { return appId; } private static Executor clientExecutor = SumkThreadPool.executor(); public static Executor clientExecutor() { return clientExecutor; } public static void resetStatus() { strated = false; } public static synchronized void init() { if (strated) { return; } try { appId = AppInfo.appId("sumk"); RpcSettings.init(); Rpc.clientExecutor = SumkServer.getExecutor("sumk.rpc.client.executor"); registryClient = IOC.getFirstBean(RegistryFactory.class, false).registryClient(); if (registryClient.isPresent()) { registryClient.get().watch(); } Transports.init(); Transports.factory().initClient(); Task.scheduleAtFixedRate(TransportClientHolder::cleanReqSession, 60_000, 60_000); strated = true; } catch (Exception e) { throw SumkException.wrap(e); } } public static Client create(String api) { return new Client(api); } /** * 根据参数顺序调用rpc方法
* 参数对象是原始的参数对象,不需要进行gson转化 * * @param api 注册的接口名称,如a.b.c * @param args 支持泛型,比如List<Integer>之类的。但不提倡使用泛型。 * @return json格式的服务器响应结果 * @throws org.yx.exception.SoaException rpc异常 * @throws org.yx.exception.BizException 业务异常 */ public static String call(String api, Object... args) { return callAsync(api, args).getOrException(); } public static String callInMap(String api, Map map) { return callInMapAsync(api, map).getOrException(); } private static T fromJson(String json, Class resultClz) { if (json == null) { return null; } return S.json().fromJson(json, resultClz); } public static T invoke(Class resultClz, String api, Object... args) { return fromJson(call(api, args), resultClz); } public static T invokeInMap(Class resultClz, String api, Map map) { return fromJson(callInMap(api, map), resultClz); } /** * 根据参数顺序异步调用rpc方法 * * @param api 注册的接口名称,如a.b.c * @param args 支持泛型,比如List<Integer>之类的。但不提倡使用泛型 * @return json格式的服务器响应结果 */ public static RpcFuture callAsync(String api, Object... args) { return create(api).paramInArray(args).execute(); } public static RpcFuture callInMapAsync(String api, Map map) { return create(api).paramInMap(map).execute(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/RpcCallInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import org.yx.common.Host; import org.yx.exception.CodeException; public class RpcCallInfo { private final RpcResult result; private final Host server; private final String sn; public RpcCallInfo(String sn, RpcResult result, Host server) { this.sn = sn; this.result = result; this.server = server; } /** * 返回值不为null * * @return 本次请求的唯一编码 */ public String getRequestId() { return this.sn; } /** * 如果是本地调用,返回local:0 * * @return 被调用的服务端地址 */ public Host getServer() { return server; } public RpcResult getResult() { return result; } public CodeException exception() { return result == null ? null : result.exception; } public String json() { return result == null ? null : result.json; } public T optResult(Class clz) { return result == null ? null : result.optResult(clz); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/RpcFuture.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import org.yx.common.Host; import org.yx.exception.CodeException; public interface RpcFuture { /** * 等待返回,直到超时 * * @return 如果没有异常,就返回信息,如果发生了异常,就抛出异常 */ String getOrException() throws CodeException; T getOrException(Class clz) throws CodeException; /** * 等待返回,直到超时 * * @return 如果发生异常就返回null * */ String opt(); T opt(Class clz); RpcResult awaitForRpcResult(); /** * 这个方法不会等待 * * @return 如果已经收到返回值,就返回。否则返回null */ RpcResult rpcResult(); String getRequestId(); Host getServer(); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/RpcFutureImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; public class RpcFutureImpl extends AbstractRpcFuture { private RpcResult result; public RpcFutureImpl(RpcLocker target) { super(target); } @Override public RpcResult awaitForRpcResult() { if (this.result != null) { return this.result; } this.result = locker.awaitForResponse(); return this.result; } @Override public RpcResult rpcResult() { return this.result; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/RpcLocker.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import java.util.function.Consumer; import org.yx.base.context.ActionContext; import org.yx.base.context.LogContext; import org.yx.common.Host; import org.yx.exception.SoaException; import org.yx.log.Logs; import org.yx.rpc.RpcErrorCode; import org.yx.rpc.log.RpcLog; import org.yx.rpc.log.RpcLogs; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.RpcWriteListener; public final class RpcLocker implements RpcWriteListener { private final AtomicReference result = new AtomicReference<>(); final Req req; private Host url; final Consumer callback; private final LogContext originLogContext; private final AtomicReference awaitThread = new AtomicReference<>(); RpcLocker(Req req, Consumer callback) { this.req = Objects.requireNonNull(req); this.callback = callback; this.originLogContext = ActionContext.current().logContext(); } public LogContext originLogContext() { return this.originLogContext; } public void url(Host url) { this.url = url; } public Host url() { return url; } public boolean isWaked() { return this.result.get() != null; } public void wakeupAndLog(RpcResult result) { this.wakeup0(result, true); } public void discard(RpcResult result) { this.wakeup0(result, false); } private void wakeup0(RpcResult result, boolean finish) { Objects.requireNonNull(result, "result cannot be null"); if (this.isWaked()) { return; } if (!this.result.compareAndSet(null, result)) { return; } if (!finish) { return; } long receiveTime = System.currentTimeMillis(); Thread thread = awaitThread.getAndSet(null); if (thread != null) { LockSupport.unpark(thread); } RpcLogs.clientLog(new RpcLog(this.url, this.req, this.originLogContext, result, receiveTime)); if (this.callback != null) { ActionContext old = ActionContext.current().clone(); try { ActionContext.store(ActionContext.newContext(this.originLogContext)); RpcCallInfo info = new RpcCallInfo(this.req.getSn(), this.result.get(), this.url); callback.accept(info); } catch (Throwable e) { Logs.rpc().error(e.getLocalizedMessage(), e); } finally { ActionContext.store(old); } } } @Override public void afterWrited(final RpcWriteFuture future) { if (future.getException() == null) { return; } Rpc.clientExecutor().execute(() -> { if (LockHolder.remove(req.getSn()) == null) { return; } if (url != null) { HostChecker.get().addDownUrl(url); } wakeupAndLog(RpcResult.sendFailed(req, future.getException())); }); } public RpcResult awaitForResponse() { RpcResult rpcResult = this.result.get(); if (rpcResult != null) { return rpcResult; } Thread currentThread = Thread.currentThread(); if (!awaitThread.compareAndSet(null, currentThread)) { throw SoaException.create(RpcErrorCode.TIMEOUT, "cannot await twice", new TimeoutException()); } while (result.get() == null) { LockSupport.parkUntil(System.currentTimeMillis() + 10000); } rpcResult = this.result.get(); if (rpcResult == null) { rpcResult = RpcResult.timeout(req); } return rpcResult; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/RpcResult.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import org.yx.common.util.S; import org.yx.exception.BizException; import org.yx.exception.CodeException; import org.yx.exception.SoaException; import org.yx.rpc.RpcErrorCode; public final class RpcResult { static RpcResult timeout(Req req) { if (req == null) { return new RpcResult(null, new SoaException(RpcErrorCode.TIMEOUT, "服务处理超时", "req is null")); } long timeout = System.currentTimeMillis() - req.getStart(); String msg = "timeout in " + timeout + "ms,sn=" + req.getSn(); SoaException exception = new SoaException(RpcErrorCode.TIMEOUT, "服务处理超时", msg); return new RpcResult(null, exception); } static RpcResult sendFailed(Req req, Throwable e) { return new RpcResult(null, parseException(e)); } static CodeException parseException(Throwable e) { if (e == null) { return null; } if (!(e instanceof CodeException)) { return SoaException.create(RpcErrorCode.UNKNOW, e.getMessage(), e); } if (SoaException.class == e.getClass()) { SoaException ex = (SoaException) e; if (ex.isBizException()) { return BizException.create(ex.getCode(), ex.getMessage()); } } return (CodeException) e; } final String json; final CodeException exception; public RpcResult(String json, CodeException exception) { this.json = exception != null ? null : json; this.exception = parseException(exception); } public CodeException exception() { return exception; } public void throwIfException() throws CodeException { if (this.exception != null) { throw exception; } } public String json() { return json; } public T optResult(Class clz) { if (this.json == null) { return null; } return S.json().fromJson(json, clz); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/TransportClientHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.yx.common.Host; import org.yx.log.Logs; import org.yx.rpc.transport.TransportClient; import org.yx.rpc.transport.Transports; public class TransportClientHolder { private static ConcurrentMap sessions = new ConcurrentHashMap<>(); public static void addClientIfAbsent(Host url, TransportClient s) { sessions.putIfAbsent(url, s); } public static boolean remove(Host url, TransportClient expect) { return sessions.remove(url, expect); } public static TransportClient getSession(Host url) { TransportClient obj = sessions.get(url); if (obj != null) { return obj; } TransportClient ses = Transports.factory().connect(url); TransportClient ses0 = sessions.putIfAbsent(url, ses); if (ses0 == null) { return ses; } ses.closeIfPossibble(); return ses0; } public static Map view() { return Collections.unmodifiableMap(sessions); } public static synchronized void cleanReqSession() { Logs.rpc().debug("begin clean idle client transport"); Map map = sessions; for (Host h : map.keySet()) { TransportClient session = map.get(h); if (session == null || !session.isIdle()) { continue; } Logs.rpc().info("remove idle client transport {}", session); map.remove(h, session); session.closeIfPossibble(); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/intf/IntfClientHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client.intf; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.yx.common.json.JsonTypes; import org.yx.common.util.S; import org.yx.rpc.client.Client; import org.yx.rpc.client.Rpc; import org.yx.rpc.spec.RpcSpecs; import org.yx.rpc.spec.SoaClientConfigSpec; import org.yx.util.ExceptionUtil; public class IntfClientHandler implements InvocationHandler { protected final String prefix; protected final Map map; public IntfClientHandler(String prefix, Class intf) { this.prefix = prefix; this.map = buildClientMap(intf); } protected Map buildClientMap(Class intf) { Map tmp = new HashMap<>(); Method[] ms = intf.getMethods(); for (Method m : ms) { SoaClientConfigSpec sc = RpcSpecs.extractSoaClientConfig(m); if (sc != null) { tmp.put(m.getName(), sc); } } return tmp.isEmpty() ? Collections.emptyMap() : tmp; } @Override public Object invoke(Object proxy, Method method, Object[] args) { if ("toString".equals(method.getName()) && (args == null || args.length == 0)) { return this.getApi(method); } try { return this.onInvoke(proxy, method, args); } catch (Throwable e) { throw ExceptionUtil.toRuntimeException(e); } } protected String getApi(Method method) { return prefix + method.getName(); } protected Object onInvoke(Object proxy, Method method, Object[] args) throws Exception { SoaClientConfigSpec sc = map.get(method.getName()); Client client = Rpc.create(getApi(method)); client.paramInArray(args); if (sc != null) { if (sc.timeout() > 0) { client.timeout(sc.timeout()); } if (sc.tryCount() > 0) { client.tryCount(sc.tryCount()); } } String json = client.execute().getOrException(); if (json == null || method.getReturnType() == Void.TYPE) { return null; } String gen = method.getGenericReturnType().getTypeName(); Type type = JsonTypes.get(gen); if (type == null) { return S.json().fromJson(json, method.getReturnType()); } return S.json().fromJson(json, type); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/intf/InvocationHandlerFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client.intf; import java.lang.reflect.InvocationHandler; import java.util.Objects; import java.util.function.Function; import org.yx.rpc.context.InnerRpcUtil; public final class InvocationHandlerFactory { private static Function, InvocationHandler> factory = intf -> { String prefix = InnerRpcUtil.parseRpcIntfPrefix(intf); return new IntfClientHandler(prefix, intf); }; public static Function, InvocationHandler> getFactory() { return factory; } public static void setFactory(Function, InvocationHandler> factory) { InvocationHandlerFactory.factory = Objects.requireNonNull(factory); } public static InvocationHandler create(Class clz) { return factory.apply(clz); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/intf/SoaClientFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client.intf; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; import org.slf4j.Logger; import org.yx.annotation.Bean; import org.yx.annotation.Exclude; import org.yx.annotation.Priority; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.base.scaner.ClassScaner; import org.yx.bean.FactoryBean; import org.yx.bean.IOC; import org.yx.bean.InterfaceBean; import org.yx.common.json.JsonTypes; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.util.Loader; import org.yx.util.StringUtil; @Priority(20000) @Bean public class SoaClientFactory implements FactoryBean { protected Set getInterfaceNames() { Predicate exclude = BooleanMatcher.FALSE; String patterns = AppInfo.getLatin("sumk.rpc.intfclient.exclude", null); if (patterns != null) { exclude = Matchers.createWildcardMatcher(patterns, 1); } String packs = AppInfo.getLatin("sumk.rpc.intfclient.package", null); Set clzNames = new HashSet<>(); if (packs != null) { String[] ps = packs.split(Const.COMMA); Collection temp = ClassScaner.listClasses(Arrays.asList(ps)); for (String c : temp) { if (exclude.test(c)) { continue; } clzNames.add(c); } } String interfaces = AppInfo.getLatin("sumk.rpc.intfclient.interface", null); if (StringUtil.isNotEmpty(interfaces)) { for (String s : interfaces.split(",")) { s = s.trim(); if (s.length() > 0) { if (exclude.test(s)) { continue; } clzNames.add(s); } } } return clzNames; } @Override public Collection beans() { if (AppInfo.getBoolean("sumk.rpc.intfclient.disable", false)) { return Collections.emptyList(); } Logger log = Logs.ioc(); List ret = new ArrayList<>(); Set clzNames = this.getInterfaceNames(); for (String c : clzNames) { try { Class intf = Loader.loadClass(c); if (!intf.isInterface() || (intf.getModifiers() & Modifier.PUBLIC) == 0) { continue; } if (intf.isAnnotationPresent(Exclude.class)) { continue; } if (IOC.get(intf) != null) { log.debug("{}接口已经有对应的bean,类型为{},就不创建微服务客户端了", intf.getName(), IOC.get(intf).getClass().getName()); continue; } Method[] ms = intf.getMethods(); if (ms == null || ms.length == 0) { log.debug("{}接口没有任何方法,被过滤掉", c); continue; } log.debug("add soa client interface {}", c); ret.add(new InterfaceBean(intf, proxyInterface(intf))); registeGenericReturnType(ms); } catch (NoClassDefFoundError e) { log.warn("soa client interface {} ignored.{}", c, e.getMessage()); } catch (Exception e) { Log.printStack("sumk.error", e); } } return ret; } private void registeGenericReturnType(Method[] ms) { for (Method m : ms) { Type genericReturnType = m.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType) { JsonTypes.registe(genericReturnType); } } } protected Object proxyInterface(Class intf) { return Proxy.newProxyInstance(Loader.loader(), new Class[] { intf }, getInvocationHandler(intf)); } protected InvocationHandler getInvocationHandler(Class intf) { return InvocationHandlerFactory.create(intf); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/client/intf/SoaClientPlugin.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.client.intf; import org.yx.annotation.Bean; import org.yx.base.context.AppContext; import org.yx.bean.Plugin; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.main.StartConstants; import org.yx.rpc.client.Rpc; @Bean public class SoaClientPlugin implements Plugin { @Override public void startAsync() { if (AppContext.inst().get(StartConstants.NOSOA_ClIENT) != null || !AppInfo.getBoolean("sumk.rpc.client.start", false)) { return; } try { Rpc.init(); } catch (NoClassDefFoundError e) { Logs.ioc().warn("soa client donot start because some class not found: {}", e.getLocalizedMessage()); } } @Override public int order() { return 9990; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/AbstractDataBuffer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; import org.yx.rpc.transport.DataBuffer; public abstract class AbstractDataBuffer implements DataBuffer { @Override public void writeBytes(byte[] bs) { this.write(bs, 0, bs.length); } @Override public void writeInt(int x, int bytes) { switch (bytes) { case 1: if (x > 0xFF) { throw new IllegalArgumentException(x + "无法序列化到1个字节里"); } this.writeBytes(new byte[] { (byte) x }); return; case 2: if (x > 0xFFFF) { throw new IllegalArgumentException(x + "无法序列化到2个字节里"); } this.writeBytes(new byte[] { (byte) (x >> 8), (byte) x }); return; case 3: if (x > 0xFFFFFF) { throw new IllegalArgumentException(x + "无法序列化到3个字节里"); } this.writeBytes(new byte[] { (byte) (x >> 16), (byte) (x >> 8), (byte) x }); return; case 4: this.writeBytes(new byte[] { (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x }); return; } throw new IllegalArgumentException("bytes必须在1-4之间"); } @Override public int readInt(int bytes) { if (bytes > 4 || bytes <= 0) { throw new IllegalArgumentException("bytes必须在1-4之间"); } byte[] bs = new byte[4]; this.read(bs, 4 - bytes, bytes); return (bs[0] << 24) | ((bs[1] & 0xff) << 16) | ((bs[2] & 0xff) << 8) | ((bs[3] & 0xff)); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/CodecKit.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; import java.util.List; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.rpc.codec.decoders.DataDecoder; import org.yx.rpc.codec.encoders.DataEncoder; import org.yx.rpc.transport.DataBuffer; public final class CodecKit { public static final String LOG_NAME = "sumk.rpc.codec"; private static DataEncoder[] encoders; private static DataDecoder[] decoders; public static void init(List encoders, List decoders) { CodecKit.encoders = encoders.toArray(new DataEncoder[encoders.size()]); CodecKit.decoders = decoders.toArray(new DataDecoder[decoders.size()]); } public static Object decode(DataBuffer in) throws Exception { if (!in.avilable(8)) { return null; } final int begin = in.position(); final int protocol = in.readInt(4); if ((protocol & 0xFF_00_00_00) != Protocols.MAGIC) { in.position(begin); throw new SumkException(3432196, "error magic," + Integer.toHexString(protocol)); } int dataSize = in.readInt(4); if (!in.avilable(dataSize)) { in.position(begin); return null; } try { for (DataDecoder decoder : decoders) { if (decoder.accept(protocol)) { return decoder.decode(protocol, in, dataSize); } } throw new SumkException(394561243, "no sumk decoder:" + Integer.toHexString(protocol)); } finally { int pos = in.position(); int limit = begin + dataSize + 8; if (pos > begin && pos < limit) { Log.get(LOG_NAME).info("position forward {} -> {}", pos, limit); in.position(limit); } else if (pos > limit) { Log.get(LOG_NAME).error("position turn back {} -> {}", pos, limit); in.position(limit); } } } public static void encode(Object message, DataBuffer buf) throws Exception { for (DataEncoder encoder : encoders) { if (encoder.encode(message, buf)) { buf.flip(); return; } } throw new SumkException(394561241, "no sumk encoder for " + message.getClass().getName()); } public static void skipPrefixedString(DataStream s, int skipSize) throws Exception { if (skipSize <= 0) { return; } for (int i = 0; i < skipSize; i++) { s.readPrefixedString(1); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/DataStream.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; public interface DataStream { /** * 用于writePrefixedString和readPrefixedString做null的处理 */ public static final String NULL = new String(new char[] { 0x00, '\n', 0x03, 'Y', 0x06, 0x00, 0x00, '6' }); void position(int pos); int position(); void writeInt(int k, int bytes); /** * 内部实现推荐用本接口的NULL常量来表示null,read的时候也要做响应的转义。 这个逻辑对使用者透明 * * @param s 可以为null * @param lengthBytes 用于存储s序列化后的长度,只能是1 2 4其中的一个 * @throws Exception 异常信息 */ void writePrefixedString(CharSequence s, int lengthBytes) throws Exception; void read(byte[] dst, int offset, int length); void write(byte[] src, int offset, int length); int readInt(int bytes); String readPrefixedString(int lengthBytes) throws Exception; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/Protocols.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; import static org.yx.rpc.codec.ReqParamType.REQ_PARAM_JSON; import static org.yx.rpc.codec.ReqParamType.REQ_PARAM_ORDER; import org.yx.conf.Const; public final class Protocols { public static final int MAGIC = 0x9A_00_00_00; public static final int REQUEST = 0x1_00_00; public static final int RESPONSE = 0x2_00_00; public static final int TEST = 0x10_00; public static final int REQUEST_PARAM_TYPES = REQ_PARAM_JSON | REQ_PARAM_ORDER; public static int profile() { return REQUEST | RESPONSE | TEST | Const.SUMK_VERSION; } public static boolean hasFeature(int protocol, int feature) { return (protocol & feature) == feature; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/ReqParamType.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; public interface ReqParamType { int REQ_PARAM_JSON = 0x01_00_00; int REQ_PARAM_ORDER = 0x02_00_00; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/Request.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; import org.yx.rpc.client.Req; public class Request extends Req { private static final long serialVersionUID = 123L; /** * 服务器端接收到req请求的时间 */ private long startInServer = System.currentTimeMillis(); public long getStartInServer() { return startInServer; } public Request() { } public Request(Req req) { this.protocol = req.protocol(); this.setApi(req.getApi()); this.setUserId(req.getUserId()); this.setFullSn(req.getSn(), req.getTraceId(), req.getSpanId()); this.setFrom(req.getFrom()); this.setStart(req.getStart()); this.setAttachments(req.getAttachments()); this.setParams(req.protocol(), req.getParams()); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/StreamAble.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec; public interface StreamAble { void writeTo(DataStream s) throws Exception; void readFrom(DataStream s) throws Exception; int getMessageType(); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/decoders/DataDecoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec.decoders; import org.yx.base.Ordered; import org.yx.rpc.transport.DataBuffer; public interface DataDecoder extends Ordered { boolean accept(int protocol); public Object decode(int protocol, DataBuffer data, int dataSize) throws Exception; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/decoders/RequestDecoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec.decoders; import org.slf4j.Logger; import org.yx.annotation.Bean; import org.yx.log.Log; import org.yx.rpc.codec.CodecKit; import org.yx.rpc.codec.Protocols; import org.yx.rpc.codec.Request; import org.yx.rpc.transport.DataBuffer; @Bean public class RequestDecoder implements DataDecoder { private Logger log = Log.get(CodecKit.LOG_NAME); @Override public boolean accept(int protocol) { return Protocols.hasFeature(protocol, Protocols.REQUEST); } @Override public Object decode(int protocol, DataBuffer data, int dataSize) throws Exception { if (log.isTraceEnabled()) { log.trace("decode {} bytes to Request", dataSize); } Request req = new Request(); req.readFrom(data); return req; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/decoders/ResponseDecoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec.decoders; import org.slf4j.Logger; import org.yx.annotation.Bean; import org.yx.log.Log; import org.yx.rpc.codec.CodecKit; import org.yx.rpc.codec.Protocols; import org.yx.rpc.server.Response; import org.yx.rpc.transport.DataBuffer; @Bean public class ResponseDecoder implements DataDecoder { private Logger log = Log.get(CodecKit.LOG_NAME); @Override public boolean accept(int protocol) { return Protocols.hasFeature(protocol, Protocols.RESPONSE); } @Override public Object decode(int protocol, DataBuffer data, int dataSize) throws Exception { if (log.isTraceEnabled()) { log.trace("decode {} bytes to Request", dataSize); } Response req = new Response(); req.readFrom(data); return req; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/encoders/AbstractEncoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec.encoders; import org.yx.conf.Const; import org.yx.rpc.codec.Protocols; import org.yx.rpc.transport.DataBuffer; public abstract class AbstractEncoder implements DataEncoder { @Override public final boolean encode(Object message, DataBuffer buffer) throws Exception { T obj = this.convert(message); if (obj == null) { return false; } int start = buffer.position(); buffer.writeInt(Protocols.MAGIC | Const.SUMK_VERSION | this.getMessageType(obj), 4); buffer.position(start + 8); this.encodeBody(obj, buffer); this.putLength(buffer, start + 4); return true; } protected abstract int getMessageType(T req); protected abstract void encodeBody(T message, DataBuffer buffer) throws Exception; protected abstract T convert(Object message); private void putLength(DataBuffer buffer, int start) { int limit = buffer.position(); buffer.position(start); buffer.writeInt(limit - start - 4, 4); buffer.position(limit); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/encoders/DataEncoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec.encoders; import org.yx.base.Ordered; import org.yx.rpc.transport.DataBuffer; public interface DataEncoder extends Ordered { boolean encode(Object message, DataBuffer buf) throws Exception; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/codec/encoders/StreamAbleEncoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.codec.encoders; import org.yx.annotation.Bean; import org.yx.rpc.codec.StreamAble; import org.yx.rpc.transport.DataBuffer; @Bean public class StreamAbleEncoder extends AbstractEncoder { @Override public void encodeBody(StreamAble req, DataBuffer buffer) throws Exception { req.writeTo(buffer); } @Override protected StreamAble convert(Object message) { if (message instanceof StreamAble) { return (StreamAble) message; } return null; } @Override protected int getMessageType(StreamAble req) { return req.getMessageType(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/context/InnerRpcUtil.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.context; import org.yx.base.context.ActionContext; import org.yx.conf.AppInfo; import org.yx.rpc.client.Req; import org.yx.util.StringUtil; public final class InnerRpcUtil { public static ActionContext rpcContext(Req req, boolean isTest) { String traceId = StringUtil.isEmpty(req.getTraceId()) ? null : req.getTraceId(); return ActionContext.newContext(req.getApi(), traceId, req.getSpanId(), req.getUserId(), isTest, req.getAttachments()); } public static String parseRpcIntfPrefix(Class intfClz) { return parseClassName2Prefix(intfClz.getName(), AppInfo.getInt("sumk.rpc.intf.name.partcount", 3)); } public static String parseClassName2Prefix(String name, int partCount) { String[] names = name.split("\\."); if (names.length <= partCount) { return name + "."; } StringBuilder sb = new StringBuilder(name.length()); for (int i = names.length - partCount; i < names.length; i++) { sb.append(StringUtil.uncapitalize(names[i])).append('.'); } return sb.toString(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/context/RpcActionNode.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.context; import java.lang.reflect.Method; import java.lang.reflect.Type; import org.yx.bean.aop.asm.MethodPojo; import org.yx.bean.aop.asm.ParamPojo; import org.yx.bean.aop.context.CalleeNode; import org.yx.exception.BizException; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.rpc.RpcErrorCode; import org.yx.rpc.RpcJson; import org.yx.rpc.codec.ReqParamType; import org.yx.rpc.codec.Request; public final class RpcActionNode extends CalleeNode { private final boolean publish; public RpcActionNode(Object obj, Method method, MethodPojo argClzInfo, int toplimit, boolean publish) { super(obj, method, argClzInfo, toplimit); this.publish = publish; } public boolean publish() { return this.publish; } public ParamPojo createJsonParamPojo(Request req) throws Throwable { if (this.params.paramLength() == 0) { return this.createEmptyParamObj(); } String args = req.getJsonedParam(); ParamPojo argObj = RpcJson.server().fromJson(args, this.params.paramClz()); return argObj == null ? this.createEmptyParamObj() : argObj; } public ParamPojo createOrderParamPojo(Request req) throws Throwable { int paramLength = params.paramLength(); ParamPojo pojo = this.createEmptyParamObj(); if (paramLength == 0) { return pojo; } String[] args = req.getParamArray(); if (args == null) { if (req.hasFeature(ReqParamType.REQ_PARAM_ORDER)) { Logs.rpc().debug("{}需要传递{}个参数,实际却是null", method.getName(), paramLength); return pojo; } throw new SumkException(12012, method.getName() + "的参数类型不对,不是order类型"); } if (args.length != paramLength) { Logs.rpc().debug("{}需要传递{}个参数,实际传递{}个", method.getName(), paramLength, args.length); } Object[] objs = new Object[paramLength]; for (int i = 0; i < paramLength; i++) { if (i >= args.length || args[i] == null) { continue; } Type paramType = this.params.getParamType(i); if (paramType == String.class) { objs[i] = args[i]; } else { objs[i] = RpcJson.server().fromJson(args[i], paramType); } } pojo.setParams(objs); return pojo; } public static void checkNode(String api, CalleeNode node) { if (node == null) { throw new SumkException(123546, "[" + api + "] is not found in this server"); } if (node.overflowThreshold()) { throw BizException.create(RpcErrorCode.THREAD_THRESHOLD_OVER, "微服务限流降级"); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/context/RpcActions.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.context; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.yx.common.action.ActInfoUtil; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.main.SumkServer; import org.yx.rpc.spec.RpcSpecs; import org.yx.rpc.spec.SoaSpec; import org.yx.util.StringUtil; public final class RpcActions { private static Map> pojoMap = new ConcurrentHashMap<>(); private static Map actMap = new ConcurrentHashMap<>(); public static Class getArgType(String method) { String m = getArgClassName(method); return pojoMap.get(m); } private static String getArgClassName(String method) { int k = method.lastIndexOf('.'); return method.substring(0, k) + "_" + method.substring(k + 1); } public static RpcActionNode getActionNode(String soaName) { return actMap.get(soaName); } public static void putActNode(String soaName, RpcActionNode actInfo) { actMap.putIfAbsent(soaName, actInfo); } public static Set soaSet() { return actMap.keySet(); } public static List publishSoaSet() { List list = new ArrayList<>(actMap.size()); for (Entry entry : actMap.entrySet()) { String api = entry.getKey(); if (needPublish(api, entry.getValue())) { list.add(api); } } return list; } static boolean needPublish(String api, RpcActionNode node) { return AppInfo.getBoolean("sumk.rpc.publish.".concat(api), node.publish()); } public static List> infos(boolean full) { if (!SumkServer.isRpcEnable()) { return Collections.emptyList(); } List names = new ArrayList<>(actMap.keySet()); List> ret = new ArrayList<>(names.size()); names.sort(null); for (String name : names) { RpcActionNode rpc = actMap.get(name); if (rpc == null) { continue; } Map map = full ? ActInfoUtil.fullInfoMap(name, rpc) : ActInfoUtil.simpleInfoMap(name, rpc); ret.add(map); SoaSpec soa = RpcSpecs.extractSoa(rpc.rawMethod()); if (soa != null) { map.put("cnName", soa.cnName()); if (StringUtil.isNotEmpty(rpc.comment())) { map.put("comment", rpc.comment()); } } if (!needPublish(name, rpc)) { map.put("publish", Boolean.FALSE); } if (rpc.toplimit() != Const.DEFAULT_TOPLIMIT) { map.put("toplimit", rpc.toplimit()); } } return ret; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/data/ApiProfile.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.data; import org.yx.util.StringUtil; public class ApiProfile { private final String name; private Integer weight; public ApiProfile(String name) { this.name = name; } public String getName() { return name; } public Integer getWeight() { return weight; } void setWeight(String w) { if (StringUtil.isEmpty(w)) { return; } this.weight = Integer.valueOf(w); } @Override public String toString() { return name; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/data/RouteDataOperator.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.data; import java.util.Map; import org.yx.common.Host; public interface RouteDataOperator { String getName(Host host); byte[] serialize(Host host, Map data) throws Exception; RouteInfo deserialize(RoutePathData data) throws Exception; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/data/RouteDataOperatorImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.data; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.yx.common.Host; import org.yx.common.util.S; import org.yx.conf.AppInfo; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.rpc.registry.RegistryConst; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class RouteDataOperatorImpl implements RouteDataOperator { private static final String SERVER = "_server"; private Logger logger = Log.get("sumk.rpc.data"); private static final String SMALL_SPLIT = "="; @Override public RouteInfo deserialize(RoutePathData data) throws IOException { final String v = new String(data.data(), AppInfo.UTF8); Map map = CollectionUtil.fillMapFromText(new HashMap<>(), v, AppInfo.LN, SMALL_SPLIT); Map methodMap = CollectionUtil.subMap(map, RegistryConst.METHODS); if (methodMap.isEmpty()) { return null; } String s = map.get(SERVER); Host host = null; if (s == null || (host = Host.create(s)) == null) { logger.error("{} 不是有效的host", s); return null; } RouteInfo info = new RouteInfo(host, data.name()); String f = map.get(RegistryConst.FEATURE); if (StringUtil.isNotEmpty(f)) { try { long fv = Long.parseLong(f, 16); int reqProtocol = (int) fv; info.setFeature(reqProtocol); } catch (Exception e) { Logs.rpc().info(f + "不能解析为数字", e); } } info.setWeight(map.get(RegistryConst.WEIGHT)); List intfs = new ArrayList<>(); for (Entry entry : methodMap.entrySet()) { String m = entry.getKey(); String value = entry.getValue(); if (m.length() == 0) { continue; } ApiProfile intf = new ApiProfile(m); intfs.add(intf); if (value != null && value.length() > 0) { Map methodProperties = CollectionUtil.loadMapFromText(value, ",", ":"); intf.setWeight(methodProperties.get(RegistryConst.WEIGHT)); } } info.setApis(intfs); if (logger.isTraceEnabled()) { logger.trace("反序列化: {}\nzk上的数据: {}\ninfo: {}", data.name(), v, S.json().toJson(info)); } return info; } @Override public byte[] serialize(Host host, Map data) throws Exception { data = new HashMap<>(data); data.put(SERVER, host.toAddressString()); String s = CollectionUtil.saveMapToText(data, AppInfo.LN, SMALL_SPLIT); if (logger.isTraceEnabled()) { logger.trace("原始数据: {}\n序列化后: {}", data, s); } return s.getBytes(AppInfo.UTF8); } @Override public String getName(Host host) { return String.join("@", AppInfo.appId(""), host.toAddressString()); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/data/RouteDataOperators.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.data; import java.util.Objects; public final class RouteDataOperators { private static RouteDataOperator inst = new RouteDataOperatorImpl(); public static RouteDataOperator inst() { return inst; } public static void setOperator(RouteDataOperator op) { RouteDataOperators.inst = Objects.requireNonNull(op); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/data/RouteInfo.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.data; import java.util.Collection; import java.util.Collections; import org.yx.common.Host; import org.yx.util.CollectionUtil; import org.yx.util.StringUtil; public class RouteInfo { private Collection apis = Collections.emptyList(); private final Host host; private final String path; private int weight; private int clientCount; private int feature; public RouteInfo(Host url, String path) { this.host = url; this.path = path; } public Collection apis() { return apis; } void setApis(Collection list) { this.apis = CollectionUtil.unmodifyList(list); } void setWeight(String w) { if (StringUtil.isEmpty(w)) { return; } this.weight = Integer.parseInt(w); } void setClientCount(String w) { if (StringUtil.isEmpty(w)) { return; } this.clientCount = Integer.parseInt(w); } void setFeature(int feature) { this.feature = feature; } public int weight() { return this.weight; } public int clientCount() { return this.clientCount; } public Host host() { return host; } public int feature() { return feature; } public String path() { return path; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/data/RoutePathData.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.data; import org.yx.exception.SumkException; public class RoutePathData { private String name; private byte[] data; public RoutePathData(String name, byte[] data) { if (name.contains("/")) { throw new SumkException(23543534, name + "应该只是当前目录,而不是全路径"); } this.name = name; this.data = data; } public String name() { return name; } public byte[] data() { return data; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/log/PlainRpcLogHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.log; import static org.yx.conf.AppInfo.LN; import org.slf4j.Logger; import org.yx.common.util.S; import org.yx.exception.SoaException; import org.yx.log.Log; import org.yx.log.LogKits; import org.yx.rpc.RpcSettings; import org.yx.rpc.client.Req; import org.yx.rpc.client.RpcResult; import org.yx.rpc.codec.ReqParamType; import org.yx.rpc.codec.Request; import org.yx.rpc.server.Response; public class PlainRpcLogHandler implements RpcLogHandler { protected boolean isLogEnable(Logger logger, long totalTime, Exception e) { if (e != null) { return logger.isErrorEnabled(); } if (logger.isDebugEnabled()) { return true; } return (logger.isInfoEnabled() && totalTime >= RpcSettings.infoTime()) || (logger.isWarnEnabled() && totalTime >= RpcSettings.warnTime()); } protected void appendParam(StringBuilder sb, Req req) { if (req.hasFeature(ReqParamType.REQ_PARAM_JSON)) { sb.append(" param(json): ").append(shortParam(req.getJsonedParam())); } else { sb.append(" param(array): ").append(shortParam(S.json().toJson(req.getParamArray()))); } } protected String shortParam(String data) { return LogKits.shorterSubfix(data, RpcSettings.maxReqLogSize()); } protected String getResult(String json) { return LogKits.shorterSubfix(json, RpcSettings.maxRespLogSize()); } @Override public void clientLog(RpcLog rpcLog) { if (RpcSettings.isClientLogDisable() || rpcLog == null) { return; } Req req = rpcLog.getReq(); if (req == null) { return; } String api = req.getApi(); if (api == null || api.isEmpty() || api.startsWith("$")) { return; } Logger logger = Log.get("sumk.rpc.log.client"); long totalTime = rpcLog.getReceiveTime() - req.getStart(); RpcResult result = rpcLog.getResult(); Exception e = result != null ? result.exception() : null; if (!this.isLogEnable(logger, totalTime, e)) { return; } StringBuilder sb = new StringBuilder(64); if (req.getTraceId() != null) { sb.append('{').append(req.getTraceId()); if (req.getSpanId() != null) { sb.append('-').append(req.getSpanId()); } sb.append("} "); } sb.append(api).append(" server:").append(rpcLog.getServer()).append(" totalTime:").append(totalTime) .append(LN); this.appendParam(sb, req); if (result != null) { if (e == null) { sb.append(LN).append(" result: ").append(getResult(result.json())); } } if (e != null) { logger.error(sb.toString(), e); } else if (totalTime >= RpcSettings.warnTime()) { logger.warn(sb.toString()); } else if (totalTime >= RpcSettings.infoTime()) { logger.info(sb.toString()); } else { logger.debug(sb.toString()); } } @Override public void serverLog(Request req, Response resp) { if (RpcSettings.isServerLogDisable()) { return; } Logger logger = Log.get("sumk.rpc.log.server"); long totalTime = -1; SoaException e = null; if (resp != null) { totalTime = resp.serviceInvokeMilTime(); e = resp.exception(); } if (!this.isLogEnable(logger, totalTime, e)) { return; } StringBuilder sb = new StringBuilder(64); if (req != null) { sb.append(req.getApi()).append(" serverTime:").append(totalTime).append(LN); this.appendParam(sb, req); } String json = resp != null ? resp.json() : null; if (e == null) { sb.append(LN).append(" result: ").append(getResult(json)); } if (resp != null && resp.isSuccess()) { if (totalTime >= RpcSettings.warnTime()) { logger.warn(sb.toString()); } else if (totalTime > RpcSettings.infoTime()) { logger.info(sb.toString()); } else { logger.debug(sb.toString()); } return; } if (e != null && e.isBizException()) { logger.warn(sb.append(LN).append(e.toString()).toString()); } else { logger.error(sb.toString(), e); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/log/RpcLog.java ================================================ package org.yx.rpc.log; import org.yx.base.context.LogContext; import org.yx.common.Host; import org.yx.rpc.client.Req; import org.yx.rpc.client.RpcResult; /** * 这里的属性,以及属性的子属性,都要只读,不要去修改它
* Req里面有开始时间。
* 全部时间都不为null * */ public class RpcLog { private final Host server; private final Req req; private final RpcResult result; private final LogContext originLogContext; private long receiveTime; public RpcLog(Host server, Req req, LogContext logContext, RpcResult result, long receiveTime) { this.server = server; this.req = req; this.result = result; this.receiveTime = receiveTime; this.originLogContext = logContext; } public long getReceiveTime() { return receiveTime; } public Host getServer() { return server; } public Req getReq() { return req; } public RpcResult getResult() { return result; } public LogContext getOriginLogContext() { return originLogContext; } @Override public String toString() { return "RpcLog [server=" + server + ", req=" + req + ", result=" + result + "]"; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/log/RpcLogHandler.java ================================================ package org.yx.rpc.log; import org.yx.rpc.codec.Request; import org.yx.rpc.server.Response; public interface RpcLogHandler { void clientLog(RpcLog log); void serverLog(Request req, Response resp); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/log/RpcLogs.java ================================================ package org.yx.rpc.log; import java.util.Objects; import org.yx.rpc.codec.Request; import org.yx.rpc.server.Response; public class RpcLogs { private static RpcLogHandler handler = new PlainRpcLogHandler(); public static RpcLogHandler getHandler() { return handler; } public static void setHandler(RpcLogHandler handler) { RpcLogs.handler = Objects.requireNonNull(handler); } public static void clientLog(RpcLog log) { handler.clientLog(log); } public static void serverLog(Request req, Response resp) { handler.serverLog(req, resp); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/monitor/RpcActionProvider.java ================================================ package org.yx.rpc.monitor; import java.util.function.BiFunction; public class RpcActionProvider implements BiFunction { @Override public Object apply(String t, String u) { return null; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/monitor/RpcMonitor.java ================================================ package org.yx.rpc.monitor; import static org.yx.common.monitor.Monitors.BLANK; import static org.yx.conf.AppInfo.LN; import org.yx.common.monitor.MessageProvider; import org.yx.rpc.context.RpcActions; import org.yx.rpc.data.RouteInfo; import org.yx.rpc.registry.client.RpcRoutes; import org.yx.util.SumkDate; public class RpcMonitor implements MessageProvider { private String rpcData() { RpcRoutes route = RpcRoutes.current(); StringBuilder sb = new StringBuilder(64).append("##rpcData").append(BLANK) .append(SumkDate.of(route.createTime())); for (RouteInfo info : route.zkDatas()) { sb.append(LN).append(info.path()).append(BLANK).append(info.host()).append(BLANK).append(info.apis()); } return sb.toString(); } @Override public Object get(String type, String key, Object param) { if ("document".equals(type)) { if ("rpc-full".equals(key)) { return RpcActions.infos(true); } if ("rpc-simple".equals(key)) { return RpcActions.infos(false); } return null; } if ("rpcData".equals(key)) { return rpcData(); } return null; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/DefaultChannelInitializerSupplier.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.slf4j.Logger; import org.yx.conf.AppInfo; import org.yx.log.Log; import org.yx.rpc.BusinessHandler; import org.yx.rpc.RpcSettings; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.timeout.IdleStateHandler; public class DefaultChannelInitializerSupplier implements Supplier> { private final ChannelHandler handler; private final boolean isServer; public DefaultChannelInitializerSupplier(BusinessHandler handler, boolean isServer) { this.handler = new NettyHandler(handler); this.isServer = isServer; } @Override public ChannelInitializer get() { return new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new IdleStateHandler( isServer ? RpcSettings.maxServerIdleTime() : RpcSettings.maxClientIdleTime(), AppInfo.getLong("sumk.rpc.idle.write", 0), 0, TimeUnit.MILLISECONDS)) .addLast(new NettyEncoder()).addLast(new NettyDecoder()).addLast(handler); Logger log = Log.get(isServer ? "sumk.rpc.server" : "sumk.rpc.client"); if (log.isDebugEnabled()) { ByteBufAllocator alloc = ch.alloc(); if (alloc != null) { log.debug("netty channel alloc: {}", alloc); } } } }; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyChannel.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.net.InetSocketAddress; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.TransportChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.util.Attribute; import io.netty.util.AttributeKey; public class NettyChannel implements TransportChannel { private final Channel channel; private volatile boolean close; private NettyChannel(Channel channel) { this.channel = channel; } public static NettyChannel create(Channel session) { Attribute attr = session.attr(AttributeKey.valueOf(TransportChannel.class.getName())); NettyChannel channel = attr.get(); if (channel == null) { channel = new NettyChannel(session); NettyChannel ch2 = attr.setIfAbsent(channel); if (ch2 != null) { return ch2; } } return channel; } @Override public InetSocketAddress getRemoteAddress() { return (InetSocketAddress) channel.remoteAddress(); } @Override public RpcWriteFuture write(Object message) { ChannelFuture future = channel.writeAndFlush(message); return new NettyWriteFuture(future); } @Override public boolean isConnected() { return (!close) && channel.isActive(); } @Override public boolean isClosing() { if (close) { return true; } return !channel.isActive() || !channel.isRegistered(); } @Override public void closeNow() { close = true; channel.close(); } @Override public void closeOnFlush() { this.closeNow(); } @Override public Object getAttribute(String key) { AttributeKey KEY = AttributeKey.valueOf(key); if (!channel.hasAttr(KEY)) { return null; } return channel.attr(KEY).get(); } @Override public void setAttribute(String key, Object value) { AttributeKey KEY = AttributeKey.valueOf(key); channel.attr(KEY).set(value); } @Override public void removeAttribute(String key) { this.setAttribute(key, null); } @Override public String toString() { return String.valueOf(channel); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyClient.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.rpc.RpcSettings; import org.yx.rpc.client.AbstractTransportClient; import org.yx.rpc.client.ClientHandler; import org.yx.rpc.transport.TransportClient; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; public class NettyClient extends AbstractTransportClient { private static Supplier connectorSupplier = new BootstrapSupplier(); public static void setConnectorSupplier(Supplier connectorSupplier) { NettyClient.connectorSupplier = Objects.requireNonNull(connectorSupplier); } public static Supplier connectorSupplier() { return connectorSupplier; } public NettyClient(Host host) { super(host); } private void connect(Bootstrap connector, long timeout) throws InterruptedException { if (channel == null || channel.isClosing()) { Logs.rpc().debug("create session for {}", addr); ChannelFuture cf = connector.connect(addr.toInetSocketAddress()); Channel se = null; if (cf.await(timeout)) { se = cf.channel(); } if (se != null) { this.channel = NettyChannel.create(se); this.channel.setAttribute(TransportClient.class.getName(), this); Log.get("sumk.rpc.client").info("built netty connetion: {}", channel); return; } cf.cancel(true); } } @Override protected void connect() throws Exception { Bootstrap connector = connectorSupplier.get(); long timeout = RpcSettings.clientDefaultTimeout() + 2000; if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) { try { if (channel != null && !channel.isClosing()) { return; } connect(connector, timeout); } finally { lock.unlock(); } } } public static class BootstrapSupplier implements Supplier { private volatile Bootstrap connector; private EventLoopGroup workerGroup; private Supplier> channelInitializerSupplier; public BootstrapSupplier() { this.channelInitializerSupplier = new DefaultChannelInitializerSupplier(new ClientHandler(), false); this.workerGroup = new NioEventLoopGroup(AppInfo.getInt("sumk.rpc.client.worker.count", 4), new DefaultThreadFactory("NettyClientWorker", true)); } @Override public Bootstrap get() { Bootstrap con = this.connector; if (con != null) { return con; } return this.create(); } private synchronized Bootstrap create() { if (this.connector != null) { return this.connector; } Bootstrap bootstrap = new Bootstrap(); bootstrap.group(workerGroup).channel(NioSocketChannel.class); NettyKit.configClient(bootstrap); bootstrap.handler(this.channelInitializerSupplier.get()); this.connector = bootstrap; return bootstrap; } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyConfigSetter.java ================================================ package org.yx.rpc.netty; import org.yx.base.Ordered; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; public interface NettyConfigSetter extends Ordered { void configServer(ServerBootstrap b); void configClient(Bootstrap b); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyDataBuffer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.nio.charset.StandardCharsets; import org.yx.exception.SumkException; import org.yx.rpc.codec.AbstractDataBuffer; import org.yx.rpc.codec.DataStream; import io.netty.buffer.ByteBuf; public class NettyDataBuffer extends AbstractDataBuffer { public static final int READ = 0; public static final int WRITE = 1; private final ByteBuf buf; private int mode; public NettyDataBuffer(ByteBuf buf, int mode) { this.buf = buf; this.mode = mode; } @Override public void flip() { if (mode == READ) { this.mode = WRITE; } else { this.mode = READ; } } @Override public void position(int pos) { if (mode == READ) { buf.readerIndex(pos); } else { buf.writerIndex(pos); } } @Override public int position() { return mode == READ ? buf.readerIndex() : buf.writerIndex(); } private void checkMode(int expect) { if (mode != expect) { throw new SumkException(35435, "mode要为" + expect + "才正确"); } } @Override public void writePrefixedString(CharSequence s, int lengthBytes) throws Exception { this.checkMode(WRITE); if (s == null) { s = NULL; } int pos = buf.writerIndex(); buf.writerIndex(pos + lengthBytes); buf.writeCharSequence(s, StandardCharsets.UTF_8); int last = buf.writerIndex(); int len = last - pos - lengthBytes; this.buf.writerIndex(pos); this.writeInt(len, lengthBytes); this.buf.writerIndex(last); } @Override public void read(byte[] dst, int offset, int length) { this.checkMode(READ); buf.readBytes(dst, offset, length); } @Override public void write(byte[] src, int offset, int length) { this.checkMode(WRITE); buf.writeBytes(src, offset, length); } @Override public String readPrefixedString(int lengthBytes) throws Exception { this.checkMode(READ); int len = this.readInt(lengthBytes); String s = buf.readCharSequence(len, StandardCharsets.UTF_8).toString(); if (DataStream.NULL.equals(s)) { return null; } return s; } @Override public boolean avilable(int length) { if (this.mode == READ) { return buf.isReadable(length); } return buf.isWritable(length); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyDecoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.util.List; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.rpc.codec.CodecKit; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; public class NettyDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { try { Object message = CodecKit.decode(new NettyDataBuffer(in, NettyDataBuffer.READ)); if (message == null) { return; } out.add(message); } catch (Exception e) { Logs.rpc().error("数据解析出错", e); throw new SumkException(346456319, "数据解析出错," + e.getMessage()); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyEncoder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import org.yx.rpc.codec.CodecKit; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import io.netty.util.internal.PlatformDependent; public class NettyEncoder extends MessageToByteEncoder { public NettyEncoder() { super(PlatformDependent.directBufferPreferred()); } @Override protected void encode(ChannelHandlerContext ctx, Object message, ByteBuf out) throws Exception { if (message == null) { return; } NettyDataBuffer buf = new NettyDataBuffer(out, NettyDataBuffer.WRITE); CodecKit.encode(message, buf); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import org.yx.log.Logs; import org.yx.rpc.BusinessHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.handler.timeout.IdleStateEvent; public class NettyHandler implements ChannelInboundHandler { private final BusinessHandler handler; public NettyHandler(BusinessHandler handler) { this.handler = handler; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Logs.rpc().info("{} : channelActive", ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { this.handler.closed(NettyChannel.create(ctx.channel())); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { this.handler.received(NettyChannel.create(ctx.channel()), msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { Logs.rpc().info("{} : will close because of idle", ctx); ctx.close(); return; } if (evt instanceof ChannelInputShutdownEvent) { Logs.rpc().info("{} : evict ChannelInputShutdownEvent", ctx); ctx.close(); return; } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { this.handler.exceptionCaught(NettyChannel.create(ctx.channel()), cause); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Logs.rpc().debug("{} : handlerAdded", ctx); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Logs.rpc().debug("{} : handlerRemoved", ctx); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { Logs.rpc().debug("{} : channelRegistered", ctx); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { Logs.rpc().debug("{} : channelUnregistered", ctx); } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { Logs.rpc().debug("{} channelWritabilityChanged", ctx); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyKit.java ================================================ package org.yx.rpc.netty; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.rpc.RpcSettings; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.ChannelOption; public final class NettyKit { public static void configServer(ServerBootstrap b) { b.option(ChannelOption.SO_BACKLOG, AppInfo.getInt("sumk.rpc.backlog", 128)) .option(ChannelOption.ALLOCATOR, new UnpooledByteBufAllocator(false)) .childOption(ChannelOption.ALLOCATOR, getByteBufAllocator()) .childOption(ChannelOption.TCP_NODELAY, AppInfo.getBoolean("sumk.rpc.tcp.nodelay", true)) .childOption(ChannelOption.SO_REUSEADDR, AppInfo.getBoolean("sumk.rpc.port.reuse", false)); NettyConfigSetter setter = IOC.getFirstBean(NettyConfigSetter.class, true); if (setter != null) { setter.configServer(b); } } public static void configClient(Bootstrap b) { b.option(ChannelOption.SO_KEEPALIVE, AppInfo.getBoolean("sumk.rpc.keepalive", true)) .option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.ALLOCATOR, getByteBufAllocator()) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, RpcSettings.clientDefaultTimeout()); NettyConfigSetter setter = IOC.getFirstBean(NettyConfigSetter.class, true); if (setter != null) { setter.configClient(b); } } private static ByteBufAllocator getByteBufAllocator() { if (AppInfo.getBoolean("sumk.rpc.memory.pool", true)) { return PooledByteBufAllocator.DEFAULT; } return UnpooledByteBufAllocator.DEFAULT; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.io.IOException; import java.net.InetSocketAddress; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.locks.LockSupport; import java.util.function.Supplier; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.rpc.server.RequestHandler; import org.yx.rpc.server.ServerHandler; import org.yx.rpc.transport.TransportServer; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; public class NettyServer implements TransportServer { private final String host; private int port; private final Supplier> channelInitSupplier; private ChannelFuture future; public NettyServer(String host, int port, List handlers) { this.port = port; this.host = host; this.channelInitSupplier = createChannelInitializer(handlers); } protected Supplier> createChannelInitializer(List handlers) { return new DefaultChannelInitializerSupplier(new ServerHandler(IOC.getBeans(RequestHandler.class)), true); } protected InetSocketAddress listenAddr(boolean randomPort) { if (randomPort) { int start = AppInfo.getInt("sumk.rpc.port.start", 10000); int end = AppInfo.getInt("sumk.rpc.port.end", 60000); port = start + ThreadLocalRandom.current().nextInt(end - start); } if (host == null || host.trim().length() == 0) { return new InetSocketAddress(port); } return new InetSocketAddress(host, port); } public synchronized void start() { if (future != null) { Logs.rpc().info("server已经启动,绑定到{}端口", port); return; } ServerBootstrap b = new ServerBootstrap(); try { EventLoopGroup bossGroup = new NioEventLoopGroup(AppInfo.getInt("sumk.rpc.server.boss.count", 1), new DefaultThreadFactory("NettyServerBoss", true)); EventLoopGroup workerGroup = new NioEventLoopGroup(AppInfo.getInt("sumk.rpc.server.worker.count", 4), new DefaultThreadFactory("NettyServerWorker", true)); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childHandler(channelInitSupplier.get()); NettyKit.configServer(b); boolean randomPort = this.port < 1; for (int i = 0; i < 50; i++) { try { InetSocketAddress addr = listenAddr(randomPort); ChannelFuture f = b.bind(addr).sync(); Log.get("sumk.rpc.server").info("rpc(netty) listening on " + addr); this.future = f; this.port = addr.getPort(); break; } catch (Exception e) { if (randomPort) { Log.get("sumk.rpc.server").info("{} was occupied,try another port...", this.port); continue; } Log.get("sumk.rpc.server").info("waiting for listening to {}: {}", port, e); int time = AppInfo.getInt("sumk.rpc.server.starting.sleep", 5000); LockSupport.parkUntil(System.currentTimeMillis() + time); } } } catch (Throwable e) { Log.get("sumk.rpc.server").error(e.getLocalizedMessage(), e); } if (this.future == null) { throw new SumkException(38057306, "start netty server failed"); } } public int getPort() { return port; } public void stop() throws IOException { if (this.future == null) { return; } future.channel().close(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyTransportFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.util.Map; import org.yx.annotation.Bean; import org.yx.base.Ordered; import org.yx.bean.IOC; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.rpc.server.RequestHandler; import org.yx.rpc.transport.TransportClient; import org.yx.rpc.transport.TransportFactory; import org.yx.rpc.transport.TransportServer; @Bean public class NettyTransportFactory implements TransportFactory { public NettyTransportFactory() { String prefix = "io.netty."; Map map = AppInfo.subMap(prefix); for (String key : map.keySet()) { System.setProperty(prefix + key, map.get(key)); } } @Override public void initClient() { NettyClient.connectorSupplier().get(); } @Override public TransportClient connect(Host serverAddr) { return new NettyClient(serverAddr); } @Override public TransportServer bind(String ip, int port) { return new NettyServer(ip, port, IOC.getBeans(RequestHandler.class)); } @Override public int order() { return Ordered.DEFAULT_ORDER + 1000; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/netty/NettyWriteFuture.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.netty; import java.util.Objects; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.RpcWriteListener; import io.netty.channel.ChannelFuture; import io.netty.util.concurrent.GenericFutureListener; public class NettyWriteFuture implements RpcWriteFuture { private final ChannelFuture future; public NettyWriteFuture(ChannelFuture future) { this.future = Objects.requireNonNull(future); } @Override public boolean isWritten() { return future.isSuccess(); } @Override public Throwable getException() { return future.cause(); } @Override public void addListener(RpcWriteListener listener) { future.addListener(new NettyWriteListener(Objects.requireNonNull(listener), this)); } private static final class NettyWriteListener implements GenericFutureListener { private final RpcWriteListener listener; private final NettyWriteFuture writeFuture; public NettyWriteListener(RpcWriteListener listener, NettyWriteFuture writeFuture) { this.listener = listener; this.writeFuture = writeFuture; } @Override public void operationComplete(ChannelFuture future) throws Exception { listener.afterWrited(writeFuture); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/RegistryConst.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry; public interface RegistryConst { String SUMK_SOA_ROOT = "/sumk_soa"; String METHODS = "$."; String METHOD_SPLIT = "#"; String FEATURE = "feature"; String WEIGHT = "weight"; String START = "start"; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/RegistryFactory.java ================================================ package org.yx.rpc.registry; import java.util.Optional; import org.yx.base.Ordered; import org.yx.rpc.registry.client.RegistryClient; import org.yx.rpc.registry.server.RegistryServer; /** * 微服务注册中心。注册中心的地址由实现类决定,但是写入到注册中心的内容不受注册中心实现类的影响 * */ public interface RegistryFactory extends Ordered { Optional registryServer(); Optional registryClient(); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/client/RegistryClient.java ================================================ package org.yx.rpc.registry.client; public interface RegistryClient { void watch() throws Exception; void stop(); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/client/RouteEvent.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.client; import org.yx.rpc.data.RouteInfo; public class RouteEvent { private final RouteEventType type; private final String nodeName; private final RouteInfo route; public String getNodeName() { return nodeName; } public RouteInfo getRoute() { return route; } public RouteEventType getType() { return type; } private RouteEvent(RouteEventType type, String nodeName, RouteInfo route) { this.type = type; this.nodeName = nodeName; this.route = route; } public static RouteEvent createEvent(String nodeName, RouteInfo info) { return new RouteEvent(RouteEventType.CREATE, nodeName, info); } public static RouteEvent deleteEvent(String nodeName) { return new RouteEvent(RouteEventType.DELETE, nodeName, null); } public static RouteEvent modifyEvent(String nodeName, RouteInfo info) { return new RouteEvent(RouteEventType.MODIFY, nodeName, info); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/client/RouteEventType.java ================================================ package org.yx.rpc.registry.client; public enum RouteEventType { CREATE, DELETE, MODIFY } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/client/RouterHolder.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.client; import java.util.Collection; import java.util.Objects; import java.util.function.BiFunction; import org.yx.common.Host; import org.yx.common.route.Router; import org.yx.common.route.Routes; import org.yx.common.route.WeightedServer; public final class RouterHolder { private static BiFunction>, Router> routerFactory = (api, servers) -> Routes.createWeightedRouter(servers); public static void set(BiFunction>, Router> factory) { RouterHolder.routerFactory = Objects.requireNonNull(factory); } public static BiFunction>, Router> get() { return RouterHolder.routerFactory; } public static Router createRouter(String api, Collection> servers) { return routerFactory.apply(api, servers); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/client/RpcRoutes.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.client; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import org.yx.common.Host; import org.yx.common.route.Router; import org.yx.common.route.WeightedServer; import org.yx.log.Log; import org.yx.rpc.data.ApiProfile; import org.yx.rpc.data.RouteInfo; import org.yx.util.CollectionUtil; public final class RpcRoutes { private final Map> rpcRoutes; private final List zkDatas; private final Map protocols; private final long createTime; private RpcRoutes(Collection zkDatas, Map> routes) { this.zkDatas = CollectionUtil.unmodifyList(Objects.requireNonNull(zkDatas)); this.rpcRoutes = Objects.requireNonNull(routes); Map p = new HashMap<>(); for (RouteInfo info : zkDatas) { p.put(info.host(), info.feature()); } this.protocols = p; this.createTime = System.currentTimeMillis(); } private static volatile RpcRoutes ROUTE = new RpcRoutes(Collections.emptyList(), Collections.emptyMap()); public static Set servers() { return new HashSet<>(ROUTE.protocols.keySet()); } public static int getServerProtocol(Host url) { Integer p = ROUTE.protocols.get(url); if (p == null) { return 0; } return p.intValue(); } public static Router getRoute(String api) { return ROUTE.rpcRoutes.get(api); } public static int routeSize() { return ROUTE.rpcRoutes.size(); } private static void _refresh(Collection rawData, Map> route) { RpcRoutes r = new RpcRoutes(rawData, route); RpcRoutes.ROUTE = r; if (Log.get("sumk.rpc.client").isTraceEnabled()) { StringBuilder sb = new StringBuilder("微服务源:"); for (RouteInfo d : rawData) { sb.append(" ").append(d.host()); } Log.get("sumk.rpc.client").trace(sb.toString()); } } private static void fillWeightedServer(Map> source, Map>> dest) { for (Entry> entry : source.entrySet()) { String m = entry.getKey(); WeightedServer serverMachine = entry.getValue(); Set> server = dest.get(m); if (server == null) { server = new HashSet<>(); dest.put(m, server); } server.add(serverMachine); } } public static synchronized void refresh(Collection datas) { Map>> map = new HashMap<>(); for (RouteInfo r : datas) { fillWeightedServer(createServerMachine(r), map); } Map> routes = new HashMap<>(); for (Entry>> entry : map.entrySet()) { String method = entry.getKey(); Set> servers = entry.getValue(); if (servers == null || servers.isEmpty()) { continue; } Router route = RouterHolder.createRouter(method, servers); if (route != null) { routes.put(method, route); } } _refresh(datas, routes); } private static Map> createServerMachine(RouteInfo data) { Map> servers = new HashMap<>(); int weight = data.weight() > 0 ? data.weight() : 100; for (ApiProfile intf : data.apis()) { WeightedServer server = new WeightedHost(data.host(), weight); servers.put(intf.getName(), server); } return servers; } public static RpcRoutes current() { return ROUTE; } public Map> routes() { return CollectionUtil.unmodifyMap(this.rpcRoutes); } public List zkDatas() { return this.zkDatas; } public long createTime() { return this.createTime; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/client/WeightedHost.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.client; import org.yx.common.Host; import org.yx.common.route.AbstractWeightedServer; import org.yx.rpc.client.HostChecker; public class WeightedHost extends AbstractWeightedServer { public WeightedHost(Host source, int weight) { super(source); this.setWeight(weight); } @Override public boolean isEnable() { return !HostChecker.get().isDowned(this.source); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/server/RegistryHelper.java ================================================ package org.yx.rpc.registry.server; import java.util.HashMap; import java.util.List; import java.util.Map; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.rpc.Profile; import org.yx.rpc.context.RpcActions; import org.yx.rpc.data.RouteDataOperators; import org.yx.rpc.registry.RegistryConst; public class RegistryHelper { public static String soaRoot() { return AppInfo.get("sumk.rpc.zk.root.server", "sumk.rpc.zk.root", RegistryConst.SUMK_SOA_ROOT); } public static String fullPath(final Host host) { StringBuilder sb = new StringBuilder().append(soaRoot()).append('/') .append(RouteDataOperators.inst().getName(host)); return sb.toString(); } public static byte[] routeData(final Host host) throws Exception { List apis = RpcActions.publishSoaSet(); final Map map = new HashMap<>(); for (String api : apis) { map.put(RegistryConst.METHODS + api, AppInfo.get("sumk.rpc.api." + api)); } map.put(RegistryConst.FEATURE, Profile.featureInHex()); map.put(RegistryConst.START, String.valueOf(System.currentTimeMillis())); map.put(RegistryConst.WEIGHT, AppInfo.get("sumk.rpc.weight", "100")); return RouteDataOperators.inst().serialize(host, map); } public static boolean needRegisterServer() { return AppInfo.getBoolean("sumk.rpc.server.register", true); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/server/RegistryServer.java ================================================ package org.yx.rpc.registry.server; import org.yx.base.Ordered; import org.yx.common.Host; /** * 微服务注册中心 * */ public interface RegistryServer extends Ordered { /** * 开启微服务对注册中心的注册及变更监听 * * @param serverHost 注册中心里的本机地址 */ void start(Host serverHost); void stop(); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/SimpleZkSerializer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.zookeeper; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.I0Itec.zkclient.serialize.ZkSerializer; import org.yx.common.util.S; public class SimpleZkSerializer implements ZkSerializer { private final Charset charset; public SimpleZkSerializer() { this(StandardCharsets.UTF_8); } public SimpleZkSerializer(Charset charset) { this.charset = charset; } @Override public byte[] serialize(Object data) { if (data == null) { return null; } if (byte[].class == data.getClass()) { return (byte[]) data; } if (String.class == data.getClass()) { return ((String) data).getBytes(charset); } return S.json().toJson(data).getBytes(charset); } @Override public Object deserialize(byte[] bytes) { return bytes; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZKSystemConfig.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.zookeeper; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.yx.conf.MultiNodeConfig; import org.yx.exception.SumkException; import org.yx.log.RawLog; import org.yx.util.CollectionUtil; /** * 用于操作zk中的appinfo对应的变量信息, node在前的优先级高 */ public class ZKSystemConfig extends MultiNodeConfig { private final List dataPaths; private final String zkUrl; public ZKSystemConfig(String zkUrl, String root, List nodes) { this.zkUrl = Objects.requireNonNull(zkUrl); if (root == null) { root = "/"; } if (!root.endsWith("/")) { root = root + "/"; } List ps = new ArrayList<>(Objects.requireNonNull(nodes).size()); for (String node : nodes) { node = node.trim(); if (node.isEmpty()) { continue; } if (node.startsWith("/")) { node = node.substring(1); } ps.add(root + node); } if (ps.isEmpty()) { throw new SumkException(4325213, "zk路径不能为空"); } this.dataPaths = CollectionUtil.unmodifyList(ps); } public List getDataPaths() { return dataPaths; } public String getZkUrl() { return zkUrl; } protected IZkDataListener listener = new IZkDataListener() { @Override public void handleDataChange(String dataPath, Object data) throws Exception { onChange(dataPath); } @Override public void handleDataDeleted(String dataPath) throws Exception { onChange(dataPath); } }; protected void onChange(String dataPath) { try { if (dataPath != null) { RawLog.info(LOG_NAME, "data in zk path " + dataPath + " changed"); } ZkClient client = ZkClientHelper.getZkClient(zkUrl); Map map = new HashMap<>(); List list = new ArrayList<>(dataPaths); Collections.reverse(list); for (String path : list) { if (!client.exists(path)) { RawLog.error(LOG_NAME, "该zk地址不存在: " + path); return; } byte[] data = client.readData(path); if (data == null || data.length == 0) { RawLog.debug(LOG_NAME, path + " is empty"); continue; } Map tmp = parse(data); map.putAll(tmp); } this.config = map; onRefresh(); } catch (Exception e) { RawLog.error(LOG_NAME, e); } } @Override protected void init() { String zkUrl = getZkUrl(); this.onChange(null); ZkClient client = ZkClientHelper.getZkClient(zkUrl); for (String path : this.dataPaths) { client.subscribeDataChanges(path, listener); } } @Override public void stop() { try { ZkClient client = ZkClientHelper.getZkClient(zkUrl); for (String path : this.dataPaths) { client.unsubscribeDataChanges(path, listener); } this.started = false; } catch (Exception e) { RawLog.error(LOG_NAME, e.getMessage(), e); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkClientHelper.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.zookeeper; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.exception.ZkNodeExistsException; import org.yx.conf.AppInfo; import org.yx.log.Logs; public final class ZkClientHelper { private static Map CLIENTS = Collections.emptyMap(); public static void makeSure(ZkClient client, final String dataPath) { int start = 0, index; while (true) { index = dataPath.indexOf("/", start + 1); if (index == start + 1) { return; } String path = dataPath; if (index > 0) { path = dataPath.substring(0, index); start = index; } if (!client.exists(path)) { try { client.createPersistent(path); } catch (Exception e) { if (!(e instanceof ZkNodeExistsException)) { Logs.system().warn(path + " create failed.", e); } } } if (index < 0 || index == dataPath.length() - 1) { return; } } } public static ZkClient getZkClient(String url) { ZkClient zk = CLIENTS.get(url); if (zk != null) { return zk; } synchronized (ZkClientHelper.class) { Map map = new HashMap<>(CLIENTS); zk = map.get(url); if (zk != null) { return zk; } zk = new ZkClient(url, AppInfo.getInt("sumk.zk.session.timeout", 8000), AppInfo.getInt("sumk.zk.connection.timeout", 20000)); zk.setZkSerializer(new SimpleZkSerializer()); map.put(url, zk); CLIENTS = map; } return CLIENTS.get(url); } public static synchronized ZkClient remove(String url) { Map map = new HashMap<>(CLIENTS); ZkClient zk = map.remove(url); if (zk == null) { return null; } CLIENTS = map; return zk; } public static Set cachedUrls() { return new HashSet<>(CLIENTS.keySet()); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkRegistryClient.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.registry.zookeeper; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import org.I0Itec.zkclient.IZkChildListener; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.slf4j.Logger; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.conf.AppInfo; import org.yx.log.Log; import org.yx.rpc.data.RouteDataOperators; import org.yx.rpc.data.RouteInfo; import org.yx.rpc.data.RoutePathData; import org.yx.rpc.registry.RegistryConst; import org.yx.rpc.registry.client.RegistryClient; import org.yx.rpc.registry.client.RouteEvent; import org.yx.rpc.registry.client.RpcRoutes; import org.yx.util.StringUtil; import org.yx.util.SumkThreadPool; public class ZkRegistryClient implements RegistryClient { private final String zkUrl; private Set childs = Collections.emptySet(); private final Predicate matcher; private final String SOA_ROOT = AppInfo.get("sumk.rpc.zk.root.client", "sumk.rpc.zk.root", RegistryConst.SUMK_SOA_ROOT); private Logger logger = Log.get("sumk.rpc.client"); private final ThreadPoolExecutor executor; private final BlockingQueue queue = new LinkedBlockingQueue<>(); public ZkRegistryClient(String zkUrl) { this.zkUrl = zkUrl; this.matcher = Matchers.includeAndExclude(AppInfo.getLatin("sumk.rpc.server.include", "*"), AppInfo.getLatin("sumk.rpc.server.exclude", null)); executor = new ThreadPoolExecutor(1, 1, 5000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1000), SumkThreadPool.createThreadFactory("rpc-client-"), new ThreadPoolExecutor.DiscardPolicy()); executor.allowCoreThreadTimeOut(true); } @Override public void watch() throws IOException { if (StringUtil.isEmpty(this.zkUrl)) { return; } List datas = new ArrayList<>(); ZkClient zk = ZkClientHelper.getZkClient(zkUrl); ZkClientHelper.makeSure(zk, SOA_ROOT); final IZkDataListener nodeListener = new IZkDataListener() { ZkRegistryClient parser = ZkRegistryClient.this; @Override public void handleDataChange(String dataPath, Object data) throws Exception { logger.trace("{} node changed", dataPath); int index = dataPath.lastIndexOf("/"); if (index > 0) { dataPath = dataPath.substring(index + 1); } RouteInfo d = RouteDataOperators.inst().deserialize(new RoutePathData(dataPath, (byte[]) data)); if (d == null || d.apis().isEmpty()) { logger.debug("{} has no interface or is invalid node", dataPath); parser.handle(RouteEvent.deleteEvent(dataPath)); return; } parser.handle(RouteEvent.modifyEvent(dataPath, d)); } @Override public void handleDataDeleted(String dataPath) throws Exception { } }; List paths = zk.subscribeChildChanges(SOA_ROOT, new IZkChildListener() { ZkRegistryClient parser = ZkRegistryClient.this; @Override public void handleChildChange(String parentPath, List currentChilds) throws Exception { if (currentChilds == null) { currentChilds = Collections.emptyList(); } List ips = filter(currentChilds); List createChilds = new ArrayList<>(); Set deleteChilds = new HashSet<>(parser.childs); for (String zkChild : ips) { boolean exist = deleteChilds.remove(zkChild); if (!exist) { createChilds.add(zkChild); } } ZkClient zkClient = ZkClientHelper.getZkClient(zkUrl); parser.childs = new HashSet<>(ips); for (String create : createChilds) { logger.trace("{} node created", create); RouteInfo d = parser.getZkNodeData(zkClient, create); if (d == null) { continue; } parser.handle(RouteEvent.createEvent(create, d)); zk.subscribeDataChanges(parentPath + "/" + create, nodeListener); } for (String delete : deleteChilds) { logger.trace("{} node deleted", delete); parser.handle(RouteEvent.deleteEvent(delete)); zk.unsubscribeDataChanges(parentPath + "/" + delete, nodeListener); } } }); if (paths == null) { paths = Collections.emptyList(); } paths = filter(paths); if (logger.isDebugEnabled()) { logger.debug("valid rpc servers: {}", paths); } this.childs = new HashSet<>(paths); for (String path : paths) { RouteInfo d = getZkNodeData(zk, path); if (d == null) { continue; } zk.subscribeDataChanges(SOA_ROOT + "/" + path, nodeListener); datas.add(d); } RpcRoutes.refresh(datas); } private List filter(List currentChilds) { if (this.matcher == BooleanMatcher.TRUE) { return currentChilds; } List ips = new ArrayList<>(currentChilds.size()); for (String ip : currentChilds) { if (this.matcher.test(ip)) { ips.add(ip); } } return ips; } private RouteInfo getZkNodeData(ZkClient zk, String path) { byte[] data = zk.readData(SOA_ROOT + "/" + path); try { return RouteDataOperators.inst().deserialize(new RoutePathData(path, data)); } catch (Exception e) { logger.error("解析" + path + "的zk数据失败", e); return null; } } public void handle(RouteEvent event) { if (event == null) { return; } queue.offer(event); this.executor.execute(() -> { if (queue.isEmpty()) { return; } synchronized (ZkRegistryClient.this) { List list = new ArrayList<>(); queue.drainTo(list); if (list.isEmpty()) { return; } Map map = new HashMap<>(); for (RouteInfo info : RpcRoutes.current().zkDatas()) { map.put(info.path(), info); } if (handleData(map, list) > 0) { RpcRoutes.refresh(map.values()); } } }); } private int handleData(Map data, List list) { int count = 0; for (RouteEvent event : list) { if (event == null) { continue; } switch (event.getType()) { case CREATE: case MODIFY: if (logger.isDebugEnabled()) { logger.debug("{}: {} {}", count, event.getType(), event.getNodeName()); if (logger.isTraceEnabled()) { logger.trace("event的接口列表:{}", event.getRoute().apis()); } } data.put(event.getNodeName(), event.getRoute()); count++; break; case DELETE: if (data.remove(event.getNodeName()) != null) { logger.debug("{}: {} {}", count, event.getType(), event.getNodeName()); count++; break; } logger.info("{}: {}已经被移除了", count, event.getNodeName()); break; default: break; } } return count; } @Override public void stop() { } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkRegistryFactory.java ================================================ package org.yx.rpc.registry.zookeeper; import java.util.Optional; import org.yx.annotation.Bean; import org.yx.base.Ordered; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.rpc.registry.RegistryFactory; import org.yx.rpc.registry.client.RegistryClient; import org.yx.rpc.registry.server.RegistryServer; import org.yx.util.StringUtil; @Bean public class ZkRegistryFactory implements RegistryFactory { private static final String ZK_URL = "sumk.zkurl"; @Override public int order() { return Ordered.DEFAULT_ORDER + 1000; } @Override public Optional registryServer() { String zkUrl = zkServerUrl(); if (StringUtil.isEmpty(zkUrl)) { Logs.rpc().warn("##因为没有配置{},所以无法注册接口到注册中心##", ZK_URL); return Optional.empty(); } return Optional.of(new ZkRegistryServer(zkUrl)); } @Override public Optional registryClient() { String zkUrl = zkClinetUrl(); if (StringUtil.isEmpty(zkUrl)) { Logs.rpc().warn("##因为没有配置{},所以只能调用本机的微服务接口##", ZK_URL); return Optional.empty(); } return Optional.of(new ZkRegistryClient(zkUrl)); } public static String zkServerUrl() { String url = AppInfo.get("sumk.rpc.zk.server"); if (url != null && url.length() > 0) { return url; } return AppInfo.get(ZK_URL); } public static String zkClinetUrl() { String url = AppInfo.get("sumk.rpc.zk.client"); if (url != null && url.length() > 0) { return url; } return AppInfo.get(ZK_URL); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/registry/zookeeper/ZkRegistryServer.java ================================================ package org.yx.rpc.registry.zookeeper; import java.util.Objects; import org.I0Itec.zkclient.IZkStateListener; import org.I0Itec.zkclient.ZkClient; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.slf4j.Logger; import org.yx.base.Ordered; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.rpc.registry.server.RegistryHelper; import org.yx.rpc.registry.server.RegistryServer; import org.yx.util.StringUtil; public class ZkRegistryServer implements RegistryServer { private final Logger logger = Log.get("sumk.rpc.registry.zookeeper"); private volatile boolean started; private Host host; private boolean needRegister; private final String zkUrl; public ZkRegistryServer(String zkUrl) { this.zkUrl = zkUrl; } @Override public int order() { return Ordered.DEFAULT_ORDER + 1000; } private static boolean soaServerEnable() { return AppInfo.getBoolean("sumk.rpc.server.register", true); } @Override public synchronized void start(Host serverHost) { if (started || host != null || StringUtil.isEmpty(zkUrl)) { return; } this.host = Objects.requireNonNull(serverHost); try { ZkClient client = zkClient(); ZkClientHelper.makeSure(client, RegistryHelper.soaRoot()); this.needRegister = soaServerEnable(); if (needRegister) { this.zkRegister.run(); logger.info("registe zk at : {}", this.host); } else { this.zkUnRegister.run(); } AppInfo.addObserver(info -> { if (!ZkRegistryServer.this.started) { logger.debug("soa server has not started"); return; } boolean serverEnable = soaServerEnable(); if (serverEnable == needRegister) { return; } try { if (serverEnable) { ZkRegistryServer.this.zkRegister.run(); logger.info("soa server [{}] enabled", host); } else { ZkRegistryServer.this.zkUnRegister.run(); logger.info("soa server [{}] disabled!!!", host); } } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); } }); started = true; } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); throw new SumkException(35334546, "soa服务启动失败"); } } private final IZkStateListener stateListener = new IZkStateListener() { @Override public void handleStateChanged(KeeperState state) throws Exception { logger.debug("zk state changed:{}", state); } @Override public void handleNewSession() throws Exception { byte[] data = createZkPathData(); ZkClientHelper.getZkClient(zkUrl).createEphemeral(fullPath(), data); logger.debug("handleNewSession"); } @Override public void handleSessionEstablishmentError(Throwable error) throws Exception { logger.error("SessionEstablishmentError#" + error.getMessage(), error); } }; private ZkClient zkClient() { return ZkClientHelper.getZkClient(zkUrl); } private final Runnable zkUnRegister = () -> { ZkClient client = zkClient(); client.unsubscribeStateChanges(stateListener); client.delete(fullPath()); }; private final Runnable zkRegister = () -> { ZkClient client = zkClient(); byte[] data = null; try { data = createZkPathData(); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); return; } zkUnRegister.run(); client.createEphemeral(fullPath(), data); client.subscribeStateChanges(stateListener); }; @Override public synchronized void stop() { if (StringUtil.isEmpty(zkUrl)) { return; } try { ZkClient client = ZkClientHelper.remove(zkUrl); if (client != null) { client.unsubscribeAll(); client.delete(fullPath()); client.close(); } } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); } started = false; } private String fullPath() { return RegistryHelper.fullPath(host); } private byte[] createZkPathData() throws Exception { return RegistryHelper.routeData(host); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/LocalRequestHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import java.util.List; import java.util.Objects; import org.yx.bean.IOC; import org.yx.exception.SoaException; import org.yx.rpc.RpcErrorCode; import org.yx.rpc.codec.Request; import org.yx.rpc.context.RpcActionNode; public class LocalRequestHandler { public static final LocalRequestHandler inst = new LocalRequestHandler(); private List handlers; public List getHandlers() { return handlers; } public void setHandlers(List handlers) { this.handlers = Objects.requireNonNull(handlers); } private LocalRequestHandler() { handlers = IOC.getBeans(RequestHandler.class); } public Response handler(Request request, RpcActionNode action) { Response resp = new Response(); try { for (RequestHandler h : this.handlers) { if (h.handle(request, resp)) { resp.serviceInvokeMilTime(System.currentTimeMillis() - request.getStartInServer()); return resp; } } } catch (Throwable e) { resp.exception(SoaException.create(RpcErrorCode.SERVER_UNKNOW, "server handler error", e)); } long begin = request.getStartInServer(); resp.serviceInvokeMilTime(System.currentTimeMillis() - begin); if (resp.exception() == null) { resp.exception(new SoaException(RpcErrorCode.NO_MAPPED_UNKNOW, "没有合适的handler", "no accepted handler")); } return resp; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/LocalRpcContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; public final class LocalRpcContext { private static final ThreadLocal CTX = new ThreadLocal<>(); public static RpcContext getCtx() { return CTX.get(); } public static void setCtx(RpcContext ctx) { CTX.set(ctx); } public static void remove() { CTX.remove(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/RequestHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import org.yx.base.Ordered; import org.yx.rpc.codec.Request; public interface RequestHandler extends Ordered { boolean handle(Request request, Response resp); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/Response.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import java.io.Serializable; import org.yx.common.util.S; import org.yx.exception.SoaException; import org.yx.exception.SumkException; import org.yx.rpc.codec.CodecKit; import org.yx.rpc.codec.DataStream; import org.yx.rpc.codec.Protocols; import org.yx.rpc.codec.StreamAble; public class Response implements StreamAble, Serializable { private static final long serialVersionUID = 1L; private static final byte BODY_TYPE_JSON = 1; private static final byte BODY_TYPE_EXCEPTION = 2; private String sn; private String json; private SoaException exception; private long _ms = -1; private int _clientProtocol; public String sn() { return sn; } public String json() { return json; } public long serviceInvokeMilTime() { return _ms; } public void sn(String sn) { this.sn = sn; } public void json(String json) { this.json = json; } public void serviceInvokeMilTime(long ms) { this._ms = ms; } public Response() { } public Response(String sn) { this.sn = sn; } public SoaException exception() { return exception; } public void exception(SoaException exception) { this.exception = exception; } public boolean isSuccess() { return this.exception == null; } public int getClientProtocol() { return _clientProtocol; } public void setClientProtocol(int clientAcceptedProtocol) { this._clientProtocol = clientAcceptedProtocol; } public void writeTo(DataStream s) throws Exception { s.writeInt(1, 1); s.writePrefixedString(this.sn, 1); s.writeInt(0, 1); if (this.exception != null) { s.writeInt(BODY_TYPE_EXCEPTION, 1); String strException = S.json().toJson(exception); s.writePrefixedString(strException, 4); return; } s.writeInt(BODY_TYPE_JSON, 1); s.writePrefixedString(json, 4); } public void readFrom(DataStream s) throws Exception { int size = s.readInt(1); this.sn = s.readPrefixedString(1); CodecKit.skipPrefixedString(s, size - 1); size = s.readInt(1); if (size > 0) { for (int i = 0; i < size; i++) { s.readPrefixedString(1); s.readPrefixedString(2); } } int type = s.readInt(1); switch (type) { case BODY_TYPE_JSON: this.json = s.readPrefixedString(4); break; case BODY_TYPE_EXCEPTION: String ex = s.readPrefixedString(4); this.exception = S.json().fromJson(ex, SoaException.class); break; default: throw new SumkException(45432239, "不支持resp的body类型:" + type); } } @Override public int getMessageType() { return Protocols.RESPONSE; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/RpcContext.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import org.yx.annotation.doc.NotNull; import org.yx.bean.aop.context.NodeContext; import org.yx.rpc.codec.Request; import org.yx.rpc.context.RpcActionNode; public class RpcContext extends NodeContext { @NotNull private final RpcActionNode node; @NotNull protected final Request req; public RpcContext(RpcActionNode node, Request req) { this.node = node; this.req = req; } public Request req() { return req; } @Override public RpcActionNode node() { return this.node; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/RpcFilter.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import java.util.Objects; import org.yx.base.Ordered; import org.yx.exception.SumkException; public abstract class RpcFilter implements Ordered { private RpcFilter next; public final void setNext(RpcFilter next) { if (this.next != null) { throw new SumkException(23431, "next已经赋值了,它是" + this.next); } this.next = Objects.requireNonNull(next); } protected final Object callNextFilter(RpcContext ctx) throws Throwable { return this.next.doFilter(ctx); } public abstract Object doFilter(RpcContext ctx) throws Throwable; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/ServerHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import java.io.IOException; import java.util.List; import java.util.concurrent.Executor; import org.slf4j.Logger; import org.yx.base.context.ActionContext; import org.yx.conf.AppInfo; import org.yx.exception.SoaException; import org.yx.log.Log; import org.yx.main.SumkServer; import org.yx.rpc.BusinessHandler; import org.yx.rpc.RpcErrorCode; import org.yx.rpc.codec.Request; import org.yx.rpc.context.InnerRpcUtil; import org.yx.rpc.log.RpcLogs; import org.yx.rpc.transport.TransportChannel; public class ServerHandler implements BusinessHandler { private final Logger log; private final RequestHandler[] handlers; private final Executor executor; public ServerHandler(List handlers) { this.log = Log.get("sumk.rpc.server"); this.handlers = handlers.toArray(new RequestHandler[handlers.size()]); this.executor = SumkServer.getExecutor("sumk.rpc.server.executor"); } @Override public void received(TransportChannel session, Object message) { this.executor.execute(() -> { Response resp = new Response(); Request req = null; try { if (message == null) { return; } if (message instanceof Request) { req = (Request) message; InnerRpcUtil.rpcContext(req, req.isTest()); for (RequestHandler h : handlers) { if (h.handle(req, resp)) { resp.serviceInvokeMilTime(System.currentTimeMillis() - req.getStartInServer()); session.write(resp); return; } } } Log.get("sumk.rpc.server").warn("unkown message type:{}", message.getClass().getName()); } catch (Throwable e) { Log.get("sumk.rpc.server").error(e.toString(), e); long begin = 0; if (req != null) { begin = req.getStartInServer(); resp.sn(req.getSn()); } resp.serviceInvokeMilTime(System.currentTimeMillis() - begin); resp.exception(SoaException.create(RpcErrorCode.SERVER_UNKNOW, "server handler error", e)); session.write(resp); } finally { RpcLogs.serverLog(req, resp); ActionContext.remove(); } }); } @Override public void exceptionCaught(TransportChannel session, Throwable cause) { session.closeOnFlush(); if (cause == null) { return; } if (cause.getClass() == IOException.class && AppInfo.getBoolean("rpc.session.print_simple_error", true)) { String msg = cause.getMessage(); if (msg != null && (msg.contains("连接") || msg.contains("connection"))) { log.debug("session:{},message:{}", session, cause.getMessage()); return; } } log.error(session + ",message:" + cause.getMessage(), cause); } @Override public void closed(TransportChannel session) { session.closeNow(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/SoaPlugin.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server; import java.lang.reflect.Constructor; import java.util.Collection; import org.yx.annotation.Bean; import org.yx.base.Lifecycle; import org.yx.bean.IOC; import org.yx.bean.InnerIOC; import org.yx.bean.Plugin; import org.yx.common.monitor.Monitors; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.main.SumkServer; import org.yx.rpc.RpcSettings; import org.yx.rpc.monitor.RpcMonitor; import org.yx.rpc.registry.RegistryFactory; import org.yx.rpc.server.impl.RpcHandler; import org.yx.rpc.server.start.SoaAnnotationResolver; import org.yx.rpc.transport.Transports; import org.yx.util.Loader; @Bean public class SoaPlugin implements Plugin { protected Lifecycle server; @Override public void startAsync() { if (!SumkServer.isRpcEnable() || SumkServer.soaPort() < 0) { return; } try { RpcSettings.init(); resolveSoaAnnotation(InnerIOC.beans()); Transports.init(); RpcHandler.init(); Monitors.add(new RpcMonitor()); RegistryFactory r = IOC.getFirstBean(RegistryFactory.class, false); String clzName = AppInfo.get("sumk.rpc.starter.class", "org.yx.rpc.server.start.SoaServer"); Class clz = Loader.loadClass(clzName); Constructor c = clz.getConstructor(RegistryFactory.class); server = (Lifecycle) c.newInstance(r); } catch (Throwable e) { Log.printStack("sumk.error", e); throw new SumkException(35345436, "rpc服务启动失败"); } } protected void resolveSoaAnnotation(Collection beans) { SoaAnnotationResolver factory = new SoaAnnotationResolver(); try { for (Object bean : beans) { factory.resolve(bean); } } catch (Exception e) { throw SumkException.wrap(e); } } @Override public void afterStarted() { if (server != null) { server.start(); } } @Override public void stop() { if (server != null) { server.stop(); server = null; } } @Override public int order() { return 10000; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/impl/JsonedParamReqHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.impl; import org.yx.annotation.Bean; import org.yx.rpc.RpcJson; import org.yx.rpc.codec.ReqParamType; import org.yx.rpc.codec.Request; import org.yx.rpc.context.RpcActionNode; import org.yx.rpc.context.RpcActions; import org.yx.rpc.server.RequestHandler; import org.yx.rpc.server.Response; import org.yx.rpc.server.RpcContext; @Bean public class JsonedParamReqHandler implements RequestHandler { @Override public boolean handle(final Request req, Response resp) { if (!req.hasFeature(ReqParamType.REQ_PARAM_JSON)) { return false; } resp.sn(req.getSn()); try { String api = req.getApi(); RpcActionNode node = RpcActions.getActionNode(api); RpcActionNode.checkNode(api, node); RpcContext ctx = new RpcContext(node, req); ctx.setParamPojo(node.createJsonParamPojo(req)); Object ret = RpcHandler.handle(ctx); resp.json(RpcJson.server().toJson(ret)); } catch (Throwable e) { ServerExceptionHandler.handle(req, resp, e); } return true; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/impl/OrderedParamReqHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.impl; import org.yx.annotation.Bean; import org.yx.rpc.RpcJson; import org.yx.rpc.codec.ReqParamType; import org.yx.rpc.codec.Request; import org.yx.rpc.context.RpcActionNode; import org.yx.rpc.context.RpcActions; import org.yx.rpc.server.RequestHandler; import org.yx.rpc.server.Response; import org.yx.rpc.server.RpcContext; @Bean public class OrderedParamReqHandler implements RequestHandler { @Override public boolean handle(Request req, Response resp) { if (!req.hasFeature(ReqParamType.REQ_PARAM_ORDER)) { return false; } resp.sn(req.getSn()); try { String api = req.getApi(); RpcActionNode node = RpcActions.getActionNode(api); RpcActionNode.checkNode(api, node); RpcContext ctx = new RpcContext(node, req); ctx.setParamPojo(node.createOrderParamPojo(req)); Object ret = RpcHandler.handle(ctx); resp.json(RpcJson.server().toJson(ret)); resp.exception(null); } catch (Throwable e) { ServerExceptionHandler.handle(req, resp, e); } return true; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/impl/RpcHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.impl; import java.util.List; import org.yx.bean.IOC; import org.yx.rpc.server.LocalRpcContext; import org.yx.rpc.server.RpcContext; import org.yx.rpc.server.RpcFilter; public final class RpcHandler { private static RpcFilter filter; private static final RpcFilter LAST = new RpcFilter() { @Override public Object doFilter(RpcContext ctx) throws Throwable { return ctx.node().execute(ctx.getParamPojo()); } }; public static synchronized void init() { if (filter != null) { return; } List list = IOC.getBeans(RpcFilter.class); if (list == null || list.isEmpty()) { filter = LAST; return; } final int size = list.size(); for (int i = 0; i < size; i++) { RpcFilter current = list.get(i); if (i == size - 1) { current.setNext(LAST); break; } current.setNext(list.get(i + 1)); } filter = list.get(0); } public static Object handle(RpcContext ctx) throws Throwable { RpcContext old = LocalRpcContext.getCtx(); LocalRpcContext.setCtx(ctx); try { return filter.doFilter(ctx); } finally { if (old == null) { LocalRpcContext.remove(); } else { LocalRpcContext.setCtx(old); } } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/impl/ServerExceptionHandler.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.impl; import org.yx.exception.SoaException; import org.yx.log.Log; import org.yx.rpc.RpcErrorCode; import org.yx.rpc.RpcSettings; import org.yx.rpc.codec.Request; import org.yx.rpc.server.Response; public final class ServerExceptionHandler { public static void handle(Request req, Response resp, Throwable e) { resp.json(null); resp.exception(SoaException.create(RpcErrorCode.SERVER_HANDLE_ERROR, e.getMessage(), e)); if (RpcSettings.showServerExceptionLog()) { Log.get("sumk.rpc.log.server.exception").error(e.toString(), e); } } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaAnnotationResolver.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.start; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.bean.BeanKit; import org.yx.bean.aop.asm.AsmUtils; import org.yx.bean.aop.asm.MethodParamInfo; import org.yx.bean.aop.asm.ParamPojos; import org.yx.conf.AppInfo; import org.yx.conf.Const; import org.yx.exception.SimpleSumkException; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.log.RawLog; import org.yx.rpc.context.RpcActionNode; import org.yx.rpc.context.RpcActions; import org.yx.rpc.spec.RpcSpecs; import org.yx.rpc.spec.SoaSpec; import org.yx.util.Loader; import org.yx.util.StringUtil; public class SoaAnnotationResolver { private SoaClassResolver soaClassResolver; private SoaNameResolver nameResolver; private Predicate exclude = BooleanMatcher.FALSE; public SoaAnnotationResolver() { nameResolver = newInstanceFromAppKey("sumk.rpc.name.resolver"); if (nameResolver == null) { nameResolver = new SoaNameResolverImpl(); } soaClassResolver = newInstanceFromAppKey("sumk.rpc.name.soaclass.resolver"); if (soaClassResolver == null) { soaClassResolver = new SoaClassResolverImpl(); } String patterns = AppInfo.get("sumk.rpc.server.exclude", null); if (patterns != null) { this.exclude = Matchers.createWildcardMatcher(patterns, 1); Logs.rpc().debug("soa server exclude:{}", this.exclude); } } private void parseSoaClass(Map map, Class targetClass, Class refer) throws NoSuchMethodException, SecurityException { String pre = this.soaClassResolver.solvePrefix(targetClass, refer); if (pre == null) { return; } Method[] methods = refer.getMethods(); for (Method m : methods) { if (m.getDeclaringClass() == Object.class) { continue; } Method methodInTarget = targetClass.getMethod(m.getName(), m.getParameterTypes()); if (!m.getReturnType().isAssignableFrom(methodInTarget.getReturnType())) { throw new SumkException(234324, targetClass.getName() + "." + methodInTarget.getName() + "的返回值类型是" + methodInTarget.getReturnType().getName() + ",期待的类型是" + m.getReturnType().getName()); } map.put(methodInTarget, pre); } } private void parseSoa(Map map, Class clz) { Method[] methods = clz.getMethods(); for (final Method m : methods) { if (RpcSpecs.extractSoa(m) == null || map.containsKey(m)) { continue; } map.putIfAbsent(m, ""); } } public void resolve(Object bean) throws Exception { Class clz = BeanKit.getTargetClass(bean); if (this.exclude.test(clz.getName())) { return; } Map map = new HashMap<>(); Class refer = this.soaClassResolver.getRefer(clz, RpcSpecs.extractSoaClass(clz)); if (refer != null) { this.parseSoaClass(map, clz, refer); } if (refer != clz) { this.parseSoa(map, clz); } List soaMethods = new ArrayList<>(map.size()); for (Method m : map.keySet()) { if (AsmUtils.notPublicOnly(m.getModifiers())) { Logs.asm().error("$$$ {}.{} has bad modifiers, maybe static or private", clz.getName(), m.getName()); continue; } soaMethods.add(m); } if (soaMethods.isEmpty()) { return; } List mpInfos = AsmUtils.buildMethodInfos(soaMethods); for (MethodParamInfo info : mpInfos) { Method m = info.getMethod(); String prefix = map.get(m); SoaSpec act = RpcSpecs.extractSoa(m); List soaNames = StringUtil.isEmpty(prefix) ? nameResolver.solve(clz, m, act) : Collections.singletonList(prefix + m.getName()); if (soaNames == null || soaNames.isEmpty()) { continue; } int toplimit = act != null && act.toplimit() > 0 ? act.toplimit() : AppInfo.getInt("sumk.rpc.toplimit.default", Const.DEFAULT_TOPLIMIT); boolean publish = act != null ? act.publish() : true; RpcActionNode node = new RpcActionNode(bean, m, ParamPojos.create(info), toplimit, publish); for (String soaName : soaNames) { if (soaName == null || soaName.isEmpty()) { continue; } if (RpcActions.getActionNode(soaName) != null) { RpcActionNode node0 = RpcActions.getActionNode(soaName); Logs.rpc().error(soaName + " already existed -- {}.{},{}.{}", node0.getDeclaringClass().getName(), node0.getMethodName(), m.getDeclaringClass().getName(), m.getName()); throw new SimpleSumkException(1242436, soaName + " already existed"); } RpcActions.putActNode(soaName, node); } } } @SuppressWarnings("unchecked") public static T newInstanceFromAppKey(String key) { String daoClz = AppInfo.get(key); if (daoClz != null && daoClz.length() > 2) { try { return (T) Loader.newInstance(daoClz); } catch (Throwable e) { RawLog.error("sumk.bean", e.getMessage(), e); return null; } } return null; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaClassResolver.java ================================================ package org.yx.rpc.server.start; import org.yx.rpc.spec.SoaClassSpec; public interface SoaClassResolver { Class AUTO = Void.class; String solvePrefix(Class targetClass, Class refer); Class getRefer(Class targetClass, SoaClassSpec sc); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaClassResolverImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.start; import java.util.function.Predicate; import org.yx.base.matcher.BooleanMatcher; import org.yx.base.matcher.Matchers; import org.yx.conf.AppInfo; import org.yx.log.Logs; import org.yx.rpc.context.InnerRpcUtil; import org.yx.rpc.spec.SoaClassSpec; import org.yx.util.Loader; public class SoaClassResolverImpl implements SoaClassResolver { private Predicate autoMatcher = BooleanMatcher.FALSE; public SoaClassResolverImpl() { String soaClassMatcher = AppInfo.get("sumk.rpc.intfserver.automatch", null); if (soaClassMatcher != null) { this.autoMatcher = Matchers.createWildcardMatcher(soaClassMatcher, 1); } } @Override public String solvePrefix(Class targetClass, Class refer) { if (refer == null) { Logs.rpc().warn("{}的@SoaClass的值不能为null或Object", targetClass.getName()); return null; } if (!refer.isAssignableFrom(targetClass)) { Logs.rpc().warn("{}的@SoaClass的value不是它的接口或超类", targetClass.getName()); return null; } return InnerRpcUtil.parseRpcIntfPrefix(refer); } @Override public Class getRefer(Class targetClass, SoaClassSpec sc) { Class refer = parseRefer(targetClass, sc); if (refer != SoaClassResolver.AUTO) { return refer; } Class[] intfs = targetClass.getInterfaces(); if (intfs != null && intfs.length == 1 && !intfs[0].getName().startsWith(Loader.JAVA_PRE)) { return intfs[0]; } else { return targetClass; } } protected Class parseRefer(Class targetClass, SoaClassSpec sc) { if (sc != null) { return sc.refer(); } if (this.autoMatcher.test(targetClass.getName())) { return SoaClassResolver.AUTO; } return null; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaNameResolver.java ================================================ package org.yx.rpc.server.start; import java.lang.reflect.Method; import java.util.List; import org.yx.rpc.spec.SoaSpec; public interface SoaNameResolver { List solve(Class clz, Method m, SoaSpec soa); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaNameResolverImpl.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.start; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.yx.conf.AppInfo; import org.yx.rpc.spec.SoaSpec; import org.yx.util.StringUtil; public class SoaNameResolverImpl implements SoaNameResolver { private final String appId; public SoaNameResolverImpl() { appId = AppInfo.getBoolean("sumk.rpc.appId.enable", true) ? AppInfo.appId(null) : null; } @Override public List solve(Class clz, Method m, SoaSpec soa) { String soaName = soa.value(); if (soaName == null || soaName.isEmpty()) { soaName = m.getName(); } String[] names = StringUtil.toLatin(soaName).split(","); List list = new ArrayList<>(names.length); for (String name : names) { name = name.trim(); if (name.isEmpty()) { continue; } list.add(name); } if (list.isEmpty()) { list.add(m.getName()); } List ret = new ArrayList<>(list.size()); for (String name : list) { ret.add(solve(name, soa)); } return ret; } public String solve(String soaName, SoaSpec soa) { StringBuilder sb = new StringBuilder(); if (soa.appIdPrefix() && this.appId != null) { sb.append(this.appId).append('.'); } return sb.append(soaName).toString(); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/server/start/SoaServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.server.start; import java.util.Optional; import org.slf4j.Logger; import org.yx.base.Lifecycle; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.log.Logs; import org.yx.main.SumkServer; import org.yx.rpc.registry.RegistryFactory; import org.yx.rpc.registry.server.RegistryServer; import org.yx.rpc.transport.TransportServer; import org.yx.rpc.transport.Transports; public class SoaServer implements Lifecycle { private volatile boolean started = false; private final Logger logger = Log.get("sumk.rpc.server"); private TransportServer server; private final Optional registry; private Host listenHost; public SoaServer(RegistryFactory registryFactory) { this.registry = registryFactory.registryServer(); } protected Host startServer() throws Exception { String ip = SumkServer.soaHost(); int port = SumkServer.soaPort(); server = Transports.factory().bind(ip, port); server.start(); return Host.create(ip, server.getPort()); } @Override public synchronized void stop() { if (this.registry.isPresent()) { try { registry.get().stop(); } catch (Exception e) { Logs.rpc().error("注册中心停止失败", e); } } if (this.server != null) { try { this.server.stop(); } catch (Exception e) { logger.error("rpc的网络服务器停止失败", e); } } started = false; } @Override public synchronized void start() { if (started) { return; } try { this.listenHost = startServer(); } catch (Exception e1) { logger.error("soa端口监听失败", e1); throw new SumkException(35334545, "soa服务启动失败"); } if (this.registry.isPresent()) { try { this.registry.get().start(getHostInRegistry()); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); throw new SumkException(35334546, "soa服务启动失败"); } } else { logger.warn("注册中心没有配置,所以没有发起注册"); } started = true; } /** * 获取写到注册中心的地址 * * @return 写到注册中心的地址 */ protected Host getHostInRegistry() { String ip_zk = soaHostInRegistry(); if (ip_zk == null) { ip_zk = listenHost.ip(); } int port_zk = soaPortInRegistry(); if (port_zk < 1) { port_zk = listenHost.port(); } return Host.create(ip_zk, port_zk); } private String soaHostInRegistry() { return AppInfo.get("sumk.rpc.registry.host", null); } private int soaPortInRegistry() { return AppInfo.getInt("sumk.rpc.registry.port", -1); } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/spec/RpcSpecs.java ================================================ package org.yx.rpc.spec; import java.lang.reflect.Method; import java.util.function.Function; import org.yx.annotation.rpc.Soa; import org.yx.annotation.rpc.SoaClass; import org.yx.annotation.rpc.SoaClientConfig; import org.yx.annotation.spec.Specs; public class RpcSpecs extends Specs { private static Function soaParser = m -> { Soa soa = m.getAnnotation(Soa.class); if (soa == null) { return null; } return new SoaSpec(soa.value(), soa.cnName(), soa.appIdPrefix(), soa.toplimit(), soa.publish()); }; private static Function, SoaClassSpec> soaClassParser = clz -> { SoaClass c = clz.getAnnotation(SoaClass.class); if (c == null) { return null; } return new SoaClassSpec(c.refer()); }; private static Function soaClientConfigParser = m -> { SoaClientConfig c = m.getAnnotation(SoaClientConfig.class); if (c == null) { return null; } return new SoaClientConfigSpec(c.timeout(), c.tryCount()); }; public static SoaSpec extractSoa(Method m) { return parse(m, soaParser); } public static SoaClassSpec extractSoaClass(Class clz) { return parse(clz, soaClassParser); } public static SoaClientConfigSpec extractSoaClientConfig(Method m) { return parse(m, soaClientConfigParser); } public static Function getSoaParser() { return soaParser; } public static void setSoaParser(Function soaParser) { RpcSpecs.soaParser = soaParser; } public static Function, SoaClassSpec> getSoaClassParser() { return soaClassParser; } public static void setSoaClassParser(Function, SoaClassSpec> soaClassParser) { RpcSpecs.soaClassParser = soaClassParser; } public static Function getSoaClientConfigParser() { return soaClientConfigParser; } public static void setSoaClientConfigParser(Function soaClientConfigParser) { RpcSpecs.soaClientConfigParser = soaClientConfigParser; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/spec/SoaClassSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.spec; import java.util.Objects; public class SoaClassSpec { private final Class refer; public SoaClassSpec(Class refer) { this.refer = Objects.requireNonNull(refer); } public Class refer() { return this.refer; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/spec/SoaClientConfigSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.spec; public class SoaClientConfigSpec { private final int timeout; private final int tryCount; public SoaClientConfigSpec(int timeout, int tryCount) { this.timeout = timeout; this.tryCount = tryCount; } public int timeout() { return this.timeout; } public int tryCount() { return this.tryCount; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/spec/SoaSpec.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.spec; public class SoaSpec { private final String value; private final String cnName; private final boolean appIdPrefix; private final int toplimit; private final boolean publish; public SoaSpec(String value, String cnName, boolean appIdPrefix, int toplimit, boolean publish) { this.value = value; this.cnName = cnName; this.appIdPrefix = appIdPrefix; this.toplimit = toplimit; this.publish = publish; } public String value() { return this.value; } public String cnName() { return this.cnName; } public boolean appIdPrefix() { return this.appIdPrefix; } public int toplimit() { return this.toplimit; } public boolean publish() { return this.publish; } } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/DataBuffer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; import org.yx.rpc.codec.DataStream; public interface DataBuffer extends DataStream { /** * 写入byte数组 * * @param bs 不能为null */ void writeBytes(byte[] bs); void flip(); boolean avilable(int length); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/RpcWriteFuture.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; public interface RpcWriteFuture { boolean isWritten(); Throwable getException(); void addListener(RpcWriteListener listener); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/RpcWriteListener.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; public interface RpcWriteListener { void afterWrited(RpcWriteFuture future); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/TransportChannel.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; import java.net.InetSocketAddress; public interface TransportChannel { InetSocketAddress getRemoteAddress(); RpcWriteFuture write(Object message); boolean isConnected(); boolean isClosing(); void closeNow(); void closeOnFlush(); Object getAttribute(String key); void setAttribute(String key, Object value); void removeAttribute(String key); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/TransportClient.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; import org.yx.common.Host; import org.yx.rpc.client.Req; public interface TransportClient { Host getRemoteAddr(); RpcWriteFuture write(Req req); void closeIfPossibble(); boolean isIdle(); TransportChannel getChannel(); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/TransportFactory.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; import org.yx.base.Ordered; import org.yx.common.Host; public interface TransportFactory extends Ordered { /** * 需要允许被多次调用 */ void initClient(); TransportClient connect(Host serverAddr); TransportServer bind(String ip, int port); } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/TransportServer.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; public interface TransportServer { void start(); int getPort(); void stop() throws Exception; } ================================================ FILE: sumk-rpc/src/main/java/org/yx/rpc/transport/Transports.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yx.rpc.transport; import org.yx.base.context.AppContext; import org.yx.bean.IOC; import org.yx.log.Logs; import org.yx.rpc.codec.CodecKit; import org.yx.rpc.codec.decoders.DataDecoder; import org.yx.rpc.codec.encoders.DataEncoder; public class Transports { private static TransportFactory factory; public static TransportFactory factory() { return factory; } public static void setFactory(TransportFactory factory) { _init(factory); } public static void init() { _init(IOC.getFirstBean(TransportFactory.class, false)); } public static synchronized void _init(TransportFactory f) { if (factory != null) { return; } try { CodecKit.init(IOC.getBeans(DataEncoder.class), IOC.getBeans(DataDecoder.class)); factory = f; } catch (Throwable e) { Logs.rpc().error("初始化rpc的TransportFactory失败", e); AppContext.startFailed(); } } } ================================================ FILE: sumk-rpc/src/main/resources/META-INF/sumk.factories ================================================ sumk.ioc.bean=org.yx.rpc.client.intf.SoaClientFactory,org.yx.rpc.client.intf.SoaClientPlugin,\ org.yx.rpc.codec.decoders.RequestDecoder,org.yx.rpc.codec.decoders.ResponseDecoder,\ org.yx.rpc.codec.encoders.StreamAbleEncoder,org.yx.rpc.netty.NettyTransportFactory,\ org.yx.rpc.registry.zookeeper.ZkRegistryFactory,org.yx.rpc.server.impl.JsonedParamReqHandler,\ org.yx.rpc.server.impl.OrderedParamReqHandler,org.yx.rpc.server.SoaPlugin sumk.ioc.optional=org.yx.rpc.* ================================================ FILE: sumk-rpc-mina/LICENSE ================================================ MIT License Copyright (c) 2021 youtongluan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: sumk-rpc-mina/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-rpc-mina com.github.youtongluan:sumk A quick developing framewort for internet company https://github.com/youtongluan/sumk The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt https://github.com/youtongluan/sumk https://github.com/youtongluan/sumk.git https://github.com/youtongluan/sumk ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh https://oss.sonatype.org/content/repositories/snapshots youtongluan 3205207767@qq.com https://www.oschina.net/p/sumk com.github.youtongluan sumk-rpc ${project.version} org.apache.mina mina-core 2.1.0 com.github.youtongluan sumk-db ${project.version} test junit junit test ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaChannel.java ================================================ package org.yx.rpc.mina; import java.net.InetSocketAddress; import org.apache.mina.core.session.IoSession; import org.yx.annotation.doc.NotNull; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.TransportChannel; public class MinaChannel implements TransportChannel { private final IoSession session; private MinaChannel(@NotNull IoSession session) { this.session = session; } public static MinaChannel create(IoSession session) { MinaChannel channel = (MinaChannel) session.getAttribute(TransportChannel.class.getName()); if (channel == null) { channel = new MinaChannel(session); channel.setAttribute(TransportChannel.class.getName(), channel); } return channel; } @Override public InetSocketAddress getRemoteAddress() { return (InetSocketAddress) session.getRemoteAddress(); } @Override public boolean isConnected() { return session.isConnected(); } @Override public void closeNow() { session.closeNow(); } @Override public void closeOnFlush() { session.closeOnFlush(); } @Override public Object getAttribute(String key) { return session.getAttribute(key); } @Override public void setAttribute(String key, Object value) { session.setAttribute(key, value); } @Override public void removeAttribute(String key) { session.removeAttribute(key); } @Override public String toString() { return "mina:" + session; } @Override public RpcWriteFuture write(Object message) { return new MinaWriteFuture(session.write(message)); } @Override public boolean isClosing() { return session.isClosing(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((session == null) ? 0 : session.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MinaChannel other = (MinaChannel) obj; if (session == null) { if (other.session != null) return false; } else if (!session.equals(other.session)) return false; return true; } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaClient.java ================================================ package org.yx.rpc.mina; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandler; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.SocketConnector; import org.apache.mina.transport.socket.nio.NioSocketConnector; import org.yx.common.Host; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Logs; import org.yx.rpc.client.AbstractTransportClient; import org.yx.rpc.client.ClientHandler; import org.yx.rpc.transport.TransportClient; public final class MinaClient extends AbstractTransportClient { private static Supplier connectorSupplier = new SocketConnectorSupplier(); public static void setConnectorSupplier(Supplier connectorSupplier) { MinaClient.connectorSupplier = Objects.requireNonNull(connectorSupplier); } public static Supplier connectorSupplier() { return connectorSupplier; } public MinaClient(Host host) { super(host); } private void connect(SocketConnector connector) throws InterruptedException { if (channel == null || channel.isClosing()) { Logs.rpc().debug("create session for {}", addr); ConnectFuture cf = connector.connect(addr.toInetSocketAddress()); cf.await(connector.getConnectTimeoutMillis() + 20); IoSession se = cf.getSession(); if (se != null) { this.channel = MinaChannel.create(se); this.channel.setAttribute(TransportClient.class.getName(), this); return; } cf.cancel(); } } @Override protected void connect() throws Exception { SocketConnector connector = connectorSupplier.get(); if (lock.tryLock(connector.getConnectTimeoutMillis() + 2000, TimeUnit.MILLISECONDS)) { try { if (channel != null && !channel.isClosing()) { return; } connect(connector); } finally { lock.unlock(); } } } public static class SocketConnectorSupplier implements Supplier { private volatile SocketConnector connector; @Override public SocketConnector get() { SocketConnector con = this.connector; if (con != null && !con.isDisposing() && !con.isDisposed()) { return con; } return this.create(); } private synchronized SocketConnector create() { if (connector != null && !connector.isDisposing() && !connector.isDisposed()) { return connector; } try { NioSocketConnector con = new NioSocketConnector( AppInfo.getInt("sumk.rpc.client.poolsize", Runtime.getRuntime().availableProcessors() + 1)); con.setConnectTimeoutMillis(AppInfo.getInt("sumk.rpc.connect.timeout", 5000)); MinaKit.config(con.getSessionConfig(), false); con.setHandler(createClientHandler()); con.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaProtocolEncoder(), new MinaProtocolDecoder())); this.connector = con; return con; } catch (Exception e) { Logs.rpc().error(e.getMessage(), e); throw new SumkException(5423654, "create connector error", e); } } protected IoHandler createClientHandler() { return new MinaHandler(new ClientHandler()); } } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaDataBuffer.java ================================================ package org.yx.rpc.mina; import org.apache.mina.core.buffer.IoBuffer; import org.yx.rpc.Profile; import org.yx.rpc.codec.AbstractDataBuffer; public class MinaDataBuffer extends AbstractDataBuffer { private final IoBuffer buf; public MinaDataBuffer(IoBuffer buf) { this.buf = buf; } public IoBuffer buffer() { return this.buf; } @Override public void writeBytes(byte[] bs) { buf.put(bs); } @Override public void flip() { buf.flip(); } @Override public void position(int pos) { buf.position(pos); } @Override public int position() { return buf.position(); } @Override public void read(byte[] dst, int offset, int length) { buf.get(dst, offset, length); } @Override public void writePrefixedString(CharSequence s, int lengthBytes) throws Exception { if (s == null) { s = NULL; } this.buf.putPrefixedString(s, lengthBytes, Profile.UTF8.newEncoder()); } @Override public String readPrefixedString(int lengthBytes) throws Exception { String s = buf.getPrefixedString(lengthBytes, Profile.UTF8.newDecoder()); return NULL.equals(s) ? null : s; } @Override public void write(byte[] bs, int offset, int length) { buf.get(bs, offset, length); } @Override public boolean avilable(int length) { return buf.remaining() >= length; } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaHandler.java ================================================ package org.yx.rpc.mina; import java.util.Objects; import org.apache.mina.core.service.IoHandler; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.FilterEvent; import org.yx.log.Logs; import org.yx.rpc.BusinessHandler; public class MinaHandler implements IoHandler { private final BusinessHandler handler; public MinaHandler(BusinessHandler handler) { this.handler = Objects.requireNonNull(handler); } @Override public void sessionCreated(IoSession session) throws Exception { } @Override public void sessionOpened(IoSession session) throws Exception { Logs.rpc().debug("open session:{}-{}", session.getServiceAddress(), session.getId()); } @Override public void sessionClosed(IoSession session) throws Exception { this.handler.closed(MinaChannel.create(session)); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { long time = System.currentTimeMillis() - session.getLastIoTime(); Logs.rpc().info("rpc session {} for {}ms,closed by this server", session, time); session.closeOnFlush(); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { this.handler.exceptionCaught(MinaChannel.create(session), cause); } @Override public void messageReceived(IoSession session, Object message) throws Exception { this.handler.received(MinaChannel.create(session), message); } @Override public void messageSent(IoSession session, Object message) throws Exception { } @Override public void inputClosed(IoSession session) throws Exception { session.closeNow(); } @Override public void event(IoSession arg0, FilterEvent arg1) throws Exception { } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaKit.java ================================================ package org.yx.rpc.mina; import java.util.HashMap; import java.util.Map; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.transport.socket.SocketSessionConfig; import org.yx.conf.AppInfo; import org.yx.conf.SimpleBeanUtil; import org.yx.log.Logs; import org.yx.rpc.RpcSettings; public final class MinaKit { public static void config(SocketSessionConfig conf, boolean server) { long maxIdle = server ? RpcSettings.maxServerIdleTime() : RpcSettings.maxClientIdleTime(); int maxIdleSecond = (int) (maxIdle / 1000); Logs.rpc().debug("max idel time for server:{} is {} second", server, maxIdleSecond); conf.setIdleTime(IdleStatus.BOTH_IDLE, maxIdleSecond); Map map = new HashMap<>(AppInfo.subMap("sumk.rpc.conf.")); String selfKey = server ? "sumk.rpc.server.conf." : "sumk.rpc.client.conf."; map.putAll(AppInfo.subMap(selfKey)); if (map.isEmpty()) { return; } String flag = server ? "server" : "client"; Logs.rpc().info(flag + " session config: {}", map); try { SimpleBeanUtil.copyProperties(conf, map); } catch (Exception e) { Logs.rpc().warn(flag + " rpc config error. " + e.getMessage(), e); } } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaProtocolDecoder.java ================================================ package org.yx.rpc.mina; import java.nio.charset.CharacterCodingException; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; import org.apache.mina.filter.codec.ProtocolDecoderException; import org.apache.mina.filter.codec.ProtocolDecoderOutput; import org.yx.log.Logs; import org.yx.rpc.codec.CodecKit; public class MinaProtocolDecoder extends CumulativeProtocolDecoder { protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws CharacterCodingException, ProtocolDecoderException { try { Object message = CodecKit.decode(new MinaDataBuffer(in)); if (message == null) { return false; } out.write(message); } catch (Exception e) { Logs.rpc().error("数据解析出错", e); throw new ProtocolDecoderException("数据解析出错," + e.getMessage()); } return true; } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaProtocolEncoder.java ================================================ package org.yx.rpc.mina; import java.nio.ByteOrder; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolEncoder; import org.apache.mina.filter.codec.ProtocolEncoderOutput; import org.yx.rpc.codec.CodecKit; public class MinaProtocolEncoder implements ProtocolEncoder { @Override public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception { if (message == null) { return; } MinaDataBuffer buf = new MinaDataBuffer(createIoBuffer(2048)); CodecKit.encode(message, buf); IoBuffer ioBuf = buf.buffer(); if (ioBuf.remaining() > 0) { out.write(ioBuf); } } @Override public void dispose(IoSession session) throws Exception { } private static IoBuffer createIoBuffer(int strLength) { return IoBuffer.allocate(strLength).setAutoExpand(true).order(ByteOrder.BIG_ENDIAN); } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaServer.java ================================================ package org.yx.rpc.mina; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.locks.LockSupport; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.core.service.IoHandler; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.SocketAcceptor; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.exception.SumkException; import org.yx.log.Log; import org.yx.rpc.server.RequestHandler; import org.yx.rpc.server.ServerHandler; import org.yx.rpc.transport.TransportServer; public class MinaServer implements TransportServer { private final String host; private int port; private IoHandler handler; private int acceptors; private SocketAcceptor acceptor; public void setAcceptors(int acceptors) { this.acceptors = acceptors; } public MinaServer(String host, int port) { this.port = port; this.host = host; this.handler = createServerHandler(); } protected IoHandler createServerHandler() { return new MinaHandler(new ServerHandler(IOC.getBeans(RequestHandler.class))); } protected InetSocketAddress listenAddr(boolean randomPort) { if (randomPort) {// 1万到6万之间 int start = AppInfo.getInt("sumk.rpc.port.start", 10000); int end = AppInfo.getInt("sumk.rpc.port.end", 60000); port = start + ThreadLocalRandom.current().nextInt(end - start); } if (host == null || host.trim().length() == 0) { return new InetSocketAddress(port); } return new InetSocketAddress(host, port); } public synchronized void start() { if (acceptor != null) { return; } try { acceptor = acceptors > 0 ? new NioSocketAcceptor(acceptors) : new NioSocketAcceptor(); acceptor.setReuseAddress(AppInfo.getBoolean("sumk.rpc.port.reuse", false)); DefaultIoFilterChainBuilder chain = acceptor.getFilterChain(); chain.addLast("codec", new ProtocolCodecFilter(new MinaProtocolEncoder(), new MinaProtocolDecoder())); acceptor.setHandler(handler); MinaKit.config(acceptor.getSessionConfig(), true); boolean randomPort = this.port < 1; for (int i = 0; i < 50; i++) { try { InetSocketAddress addr = listenAddr(randomPort); acceptor.bind(addr); Log.get("sumk.rpc.server").info("rpc(mina) listening on " + addr); break; } catch (IOException e) { if (randomPort) { Log.get("sumk.rpc.server").info("{} was occupied,try another port...", this.port); continue; } Log.get("sumk.rpc.server").debug("waiting for listening to {}.{}", port, e.getMessage()); int time = AppInfo.getInt("sumk.rpc.server.starting.sleep", 5000); LockSupport.parkUntil(System.currentTimeMillis() + time); } } } catch (Exception e) { Log.get("sumk.rpc.server").debug(e.getLocalizedMessage(), e); acceptor = null; throw new SumkException(38057306, "start mina server failed", e); } } public int getPort() { return port; } public void stop() throws IOException { if (this.acceptor == null) { return; } this.acceptor.dispose(false); this.acceptor = null; } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaTransportFactory.java ================================================ package org.yx.rpc.mina; import org.yx.annotation.Bean; import org.yx.common.Host; import org.yx.rpc.transport.TransportClient; import org.yx.rpc.transport.TransportFactory; import org.yx.rpc.transport.TransportServer; @Bean public class MinaTransportFactory implements TransportFactory { @Override public TransportClient connect(Host serverAddr) { return new MinaClient(serverAddr); } @Override public TransportServer bind(String ip, int port) { return new MinaServer(ip, port); } @Override public void initClient() { MinaClient.connectorSupplier().get(); } } ================================================ FILE: sumk-rpc-mina/src/main/java/org/yx/rpc/mina/MinaWriteFuture.java ================================================ package org.yx.rpc.mina; import java.util.Objects; import org.apache.mina.core.future.IoFutureListener; import org.apache.mina.core.future.WriteFuture; import org.yx.rpc.transport.RpcWriteFuture; import org.yx.rpc.transport.RpcWriteListener; public class MinaWriteFuture implements RpcWriteFuture { private final WriteFuture future; public MinaWriteFuture(WriteFuture future) { this.future = Objects.requireNonNull(future); } @Override public boolean isWritten() { return future.isWritten(); } @Override public Throwable getException() { return future.getException(); } @Override public void addListener(RpcWriteListener listener) { future.addListener(new MinaWriteListener(Objects.requireNonNull(listener), this)); } private static final class MinaWriteListener implements IoFutureListener { private final RpcWriteListener listener; private final MinaWriteFuture writeFuture; public MinaWriteListener(RpcWriteListener listener, MinaWriteFuture writeFuture) { this.listener = listener; this.writeFuture = writeFuture; } @Override public void operationComplete(WriteFuture future) { listener.afterWrited(writeFuture); } } } ================================================ FILE: sumk-rpc-mina/src/main/resources/META-INF/sumk.factories ================================================ sumk.ioc.bean=org.yx.rpc.mina.MinaTransportFactory sumk.ioc.exclude=org.yx.rpc.netty.NettyTransportFactory ================================================ FILE: sumk-rpc-mina/src/test/java/org/test/Main.java ================================================ package org.test; import org.yx.log.Log; import org.yx.main.SumkServer; public class Main { public static void main(String[] args) { try { Log.get(Main.class).info("需要在外部启动一个zookeeper服务器"); long begin=System.currentTimeMillis(); SumkServer.start(Main.class,null); System.out.println("启动耗时:"+(System.currentTimeMillis()-begin)+"毫秒"); Thread.currentThread().join(); } catch (Exception e) { Log.printStack("main",e); } } } ================================================ FILE: sumk-rpc-mina/src/test/java/org/test/RpcPressTest.java ================================================ package org.test; import java.util.Arrays; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.yx.log.LogLevel; import org.yx.log.Loggers; import org.yx.main.StartConstants; import org.yx.main.SumkServer; import org.yx.rpc.client.LockHolder; import org.yx.rpc.client.Rpc; import org.yx.util.SumkDate; public class RpcPressTest { @Before public void before(){ SumkServer.start(RpcPressTest.class,Arrays.asList(StartConstants.NOHTTP,StartConstants.NOSOA)); Loggers.setDefaultLevel(LogLevel.ERROR);//这个只能修改默认级别的,如果有具体设置了日志级别,它的优先级比这个高 Rpc.init(); } @After public void after(){ Assert.assertEquals(0, LockHolder.lockSize()); System.out.println("锁状态正确!"); } Random r=new Random(); @Test public void test() throws InterruptedException { System.out.println("开始压测,请耐心等待30秒左右。。。"); final int count=200000; final int threadCount=50; AtomicInteger failCount=new AtomicInteger(); CountDownLatch down=new CountDownLatch(count); Rpc.call("a.b.repeat", "预热"); char[] cs=new char[1200]; Arrays.fill(cs, 't'); Arrays.fill(cs, 100,200,'好'); cs[5]='啊'; cs[45]='1'; cs[59]='o'; cs[115]='是'; cs[800]='是'; String pre=new String(cs); long begin=System.currentTimeMillis(); AtomicLong totalRT=new AtomicLong(); for(int i=0;i names = Arrays.asList("游夏", "游侠"); String echo = "how are you"; // ret是json格式 String ret = Rpc.call(soaName("echo"), echo, names); System.out.println("result:" + ret); Assert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class)); for (int i = 0; i < 5; i++) { Map map = new HashMap<>(); map.put("echo", echo); map.put("names", names); ret = Rpc.callInMap(soaName("echo"), map); Assert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class)); ret = Rpc.call(soaName("echo"), echo, names); Assert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class)); System.out.println("test:" + ret); } } Random r = new Random(); @Test public void async() { System.out.println("now:" + System.currentTimeMillis()); List names = Arrays.asList("游夏", "游侠"); String echo = "how are you,"; // ret是json格式 List retList = new ArrayList<>(); List retList2 = new ArrayList<>(); for (int i = 0; i < 5; i++) { Map map = new HashMap<>(); map.put("echo", echo + i); map.put("names", names); retList.add(Rpc.callInMapAsync(soaName("echo"), map)); retList2.add(Rpc.callAsync(soaName("echo"), echo + i, names)); } for (int i = 0; i < 5; i++) { System.out.println(i + " 异步"); Assert.assertEquals(new EchoAction().echo(echo + i, names), S.json().fromJson(retList.get(i).opt(), List.class)); Assert.assertEquals(new EchoAction().echo(echo + i, names), S.json().fromJson(retList2.get(i).opt(), List.class)); } } } ================================================ FILE: sumk-rpc-mina/src/test/java/org/test/inner/pojo/DemoUser.java ================================================ package org.test.inner.pojo; import java.util.Date; import org.yx.annotation.db.Column; import org.yx.annotation.db.SoftDelete; import org.yx.annotation.db.Table; import org.yx.db.enums.ColumnType; import org.yx.util.SumkDate; @Table @SoftDelete(value = "enable", type = Byte.class) public class DemoUser { @Column(type = ColumnType.ID_BOTH) private Long id; private String name; private Integer age; private Date lastUpdate; public Date getLastUpdate() { return lastUpdate; } public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "DemoUser [id=" + id + ", name=" + name + ", age=" + age + ", lastUpdate=" + (lastUpdate==null ? "null" : SumkDate.of(lastUpdate)).toString() + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((age == null) ? 0 : age.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DemoUser other = (DemoUser) obj; if (age == null) { if (other.age != null) return false; } else if (!age.equals(other.age)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } ================================================ FILE: sumk-rpc-mina/src/test/java/org/test/inner/pojo/Multikey.java ================================================ package org.test.inner.pojo; import org.yx.annotation.db.Column; import org.yx.annotation.db.Table; import org.yx.db.enums.ColumnType; @Table public class Multikey { @Column(type = ColumnType.ID_BOTH, order = 1) private String id1; @Column(type = ColumnType.ID_BOTH, order = 2) private String id2; private String name; private Integer age; public String getId1() { return id1; } public Multikey setId1(String id1) { this.id1 = id1; return this; } public String getId2() { return id2; } public Multikey setId2(String id2) { this.id2 = id2; return this; } public String getName() { return name; } public Multikey setName(String name) { this.name = name; return this; } public Integer getAge() { return age; } public Multikey setAge(Integer age) { this.age = age; return this; } @Override public String toString() { return "Multikey [id1=" + id1 + ", id2=" + id2 + ", name=" + name + ", age=" + age + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((age == null) ? 0 : age.hashCode()); result = prime * result + ((id1 == null) ? 0 : id1.hashCode()); result = prime * result + ((id2 == null) ? 0 : id2.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Multikey other = (Multikey) obj; if (age == null) { if (other.age != null) return false; } else if (!age.equals(other.age)) return false; if (id1 == null) { if (other.id1 != null) return false; } else if (!id1.equals(other.id1)) return false; if (id2 == null) { if (other.id2 != null) return false; } else if (!id2.equals(other.id2)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } ================================================ FILE: sumk-rpc-mina/src/test/java/org/test/soa/demo/EchoAction.java ================================================ package org.test.soa.demo; import java.util.ArrayList; import java.util.List; import org.yx.annotation.Bean; import org.yx.annotation.rpc.Soa; @Bean public class EchoAction { @Soa("a.b.repeat") public String repeat(String s){ return s; } @Soa public List echo(String echo, List names) { List list = new ArrayList(); for (String name : names) { list.add(echo + " " + name); } return list; } @Soa public String hi() { return "hello"; } @Soa public void print() { System.out.println("print"); } } ================================================ FILE: sumk-rpc-mina/src/test/java/org/test/soa/server/SOAServer.java ================================================ package org.test.soa.server; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.yx.util.UUIDSeed; public class SOAServer { private static ZooKeeperServer zk; private static NIOServerCnxnFactory nioFactory; public static void startZKServer() throws IOException, InterruptedException{ startZKServer(500,2000); } public static void stopZKServer(){ if(zk!=null){ zk.shutdown(); } } public static void startZKServer(int tickTime, int minSessionTimeout) throws IOException, InterruptedException{ String temp=System.getProperty("java.io.tmpdir"); File f=new File(temp,UUIDSeed.seq()); f.mkdir(); zk = new ZooKeeperServer(f, f, tickTime); zk.setMinSessionTimeout(minSessionTimeout); nioFactory = new NIOServerCnxnFactory(); int maxClientConnections = 0; // 0 means unlimited nioFactory.configure(new InetSocketAddress("127.0.0.1",2181), maxClientConnections); nioFactory.startup(zk); } } ================================================ FILE: sumk-rpc-mina/src/test/resources/app.properties ================================================ sumk.zkurl=127.0.0.1:2181 #used in RPC sumk.rpc.port=0 ================================================ FILE: sumk-test/LICENSE ================================================ MIT License Copyright (c) 2021 youtongluan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: sumk-test/pom.xml ================================================ 4.0.0 com.github.youtongluan sumk 4.2.1 sumk-test com.github.youtongluan sumk-rpc ${project.version} test com.github.youtongluan sumk-http ${project.version} test com.github.youtongluan sumk-db ${project.version} test com.github.youtongluan async-logger ${project.version} test junit junit test org.apache.httpcomponents httpasyncclient test commons-logging commons-logging org.apache.httpcomponents httpmime test ================================================ FILE: sumk-test/src/test/java/org/test/Main.java ================================================ package org.test; import org.yx.log.Log; import org.yx.main.SumkServer; public class Main { public static void main(String[] args) { try { // 新版zk不好启动服务端,需要使用外置zk服务器 // Log.get(Main.class).info("为了测试方便,测试环境内置了zookeeper服务器。"); // Log.get(Main.class).info("现在开始启动内置zookeeper。。。"); // SOAServer.startZKServer(); // Log.get(Main.class).info("zookeeper启动完成,现在开始启动真正的sumk服务器。。。"); long begin=System.currentTimeMillis(); SumkServer.start(Main.class,null); System.out.println("启动耗时:"+(System.currentTimeMillis()-begin)+"毫秒"); Thread.currentThread().join(); } catch (Exception e) { Log.printStack("main",e); } } } ================================================ FILE: sumk-test/src/test/java/org/test/client/HttpAesClientTest.java ================================================ package org.test.client; import java.net.URLEncoder; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.junit.Assert; import org.junit.Test; import org.test.inner.web.client.Encrypt; import org.yx.common.util.S; import org.yx.log.Log; /* * 通过这个类,可以了解web的通讯。 * 加解密部分需要些技术功底才好看明白。 * 好消息是:现在web应用一般都使用https了,就可以不用加解密方式了,就可以不看加解密部分 */ public class HttpAesClientTest { private String getUrl(String act) { String url = "http://localhost:8080/rest/" + act; System.out.println("本次请求的url: "+url); return url; } //加密传输的例子。展示了使用表单以及不使用表单两种方式 @Test public void aes_base64() throws Exception { String charset = "UTF-8"; HttpClient client = HttpClientBuilder.create().build(); HttpResponse resp = login(client); String key_str = resp.getFirstHeader("skey").getValue(); Log.get("login").info("key:{}", key_str); byte[] key = Base64.getMimeDecoder().decode(key_str); String act = "aes_base64"; HttpPost post = new HttpPost(getUrl(act)); Map json = new HashMap<>(); json.put("echo", "你好!!!"); json.put("names", Arrays.asList("小明", "小张")); byte[] conts = Encrypt.encrypt(S.json().toJson(json).getBytes(charset), key); String req = Base64.getEncoder().encodeToString(conts); System.out.println("req:" + req); post.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"); StringEntity se = new StringEntity("data=" + URLEncoder.encode(req, "ASCII"), charset); post.setEntity(se); resp = client.execute(post); String line = resp.getStatusLine().toString(); Log.get( "aes_base64").info(line); Assert.assertEquals("HTTP/1.1 200 OK", line); HttpEntity resEntity = resp.getEntity(); String raw = EntityUtils.toString(resEntity); Log.get("aes").info("raw resp:{}", raw); byte[] contentBytes = Base64.getMimeDecoder().decode(raw); String ret = new String(Encrypt.decrypt(contentBytes, key), charset); Log.get( "aes_base64").info("服务器返回:" + ret); Assert.assertEquals("[\"你好!!! 小明\",\"你好!!! 小张\"]", ret); /* * 非表单MIME的方式提交,去掉application/x-www-form-urlencoded,内容也不要URLEncoder编码 */ post = new HttpPost(getUrl(act)); System.out.println("req:" + req); se = new StringEntity("data=" + req, charset); post.setEntity(se); resp = client.execute(post); line = resp.getStatusLine().toString(); Log.get( "aes_base64").info(line); Assert.assertEquals("HTTP/1.1 200 OK", line); resEntity = resp.getEntity(); raw = EntityUtils.toString(resEntity); Log.get("aes").info("raw resp:{}", raw); contentBytes = Base64.getMimeDecoder().decode(raw); ret = new String(Encrypt.decrypt(contentBytes, key), charset); Log.get( "aes_base64").info("服务器返回:" + ret); Assert.assertEquals("[\"你好!!! 小明\",\"你好!!! 小张\"]", ret); post = new HttpPost(getUrl("bizError")); resp = client.execute(post); Assert.assertEquals(550, resp.getStatusLine().getStatusCode()); resEntity = resp.getEntity(); String errMsg = EntityUtils.toString(resEntity); Log.get("aes").info("raw resp:{}", errMsg); Assert.assertEquals("{\"code\":\"12345\",\"message\":\"业务异常\"}", errMsg); } //测试加密传输并且签名 @Test public void aes_sign() throws Exception { String charset = "UTF-8"; String act = "aes_sign"; //登陆,并获取加密用的key[] HttpClient client = HttpClientBuilder.create().build(); HttpResponse resp = login(client); String logined = EntityUtils.toString(resp.getEntity()); System.out.println("logined:"+logined); String key_str = resp.getFirstHeader("skey").getValue(); Log.get("login").info("key:{}", key_str); byte[] key = Base64.getMimeDecoder().decode(key_str); //加密数据 Map map = new HashMap<>(); map.put("name", "小明"); byte[] conts = Encrypt.encrypt(S.json().toJson(map).getBytes(charset), key);//用AES加密 String req = Base64.getEncoder().encodeToString(conts);//变成base64 StringEntity se = new StringEntity(req, charset); Log.get("aes_sign").info("req:" + req); //生成sign,这个是需要sign的接口特有的 String sign = Encrypt.sign(S.json().toJson(map).getBytes(charset)); System.out.println("sign:" + sign); HttpPost post = new HttpPost(getUrl(act) + "?__sign=" + sign); post.setEntity(se); resp = client.execute(post); String line = resp.getStatusLine().toString(); Log.get( "aes_sign").info(line); HttpEntity resEntity = resp.getEntity(); String raw = EntityUtils.toString(resEntity); Log.get("aes").info("raw resp:{}", raw); byte[] contentBytes = Base64.getMimeDecoder().decode(raw); String ret = new String(Encrypt.decrypt(contentBytes, key), charset); Log.get( "aes_base64").info("服务器返回:" + ret); Assert.assertEquals("hello 小明", ret); } private HttpResponse login(HttpClient client) throws Exception { HttpGet post = new HttpGet("http://localhost:8080/login?username=admin&password=123456&code=9999"); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); Assert.assertTrue(line, resp.getStatusLine().getStatusCode() == 200); return resp; } } ================================================ FILE: sumk-test/src/test/java/org/test/client/HttpPlainClientTest.java ================================================ package org.test.client; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.junit.Assert; import org.junit.Test; import org.test.inner.po.DemoUser; import org.test.inner.web.client.Encrypt; import org.yx.common.util.S; import org.yx.log.Log; /* * 通过这个类,可以了解web的通讯。 */ public class HttpPlainClientTest { private String getUrl(String act) { return "http://localhost:8080/rest/" + act; } private String getUploadUrl(String act) { return "http://localhost:8080/upload/" + act; } private HttpResponse login(HttpClient client) throws Exception { HttpGet post = new HttpGet("http://localhost:8080/login?username=admin&password=123456&code=9999"); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); Assert.assertTrue(line, resp.getStatusLine().getStatusCode() == 200); return resp; } Random r = new Random(); @Test public void plain() throws IOException { String charset = "GBK"; HttpClient client = HttpClientBuilder.create().build(); String act = "echo"; HttpPost post = new HttpPost(getUrl(act)); Map json = new HashMap<>(); json.put("echo", "你好!!!"); json.put("names", Arrays.asList("小明", "小张")); StringEntity se = new StringEntity(S.json().toJson(json), charset); post.setEntity(se); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); Assert.assertEquals("HTTP/1.1 200 OK", line); HttpEntity resEntity = resp.getEntity(); String ret = EntityUtils.toString(resEntity, charset); Assert.assertEquals("[\"你好!!! 小明\",\"你好!!! 小张\"]", ret); } //测试需要登录的情况 @Test public void login_sign() throws Exception { String charset = "UTF-8"; HttpClient client = HttpClientBuilder.create().build(); login(client); String act = "plain_sign"; Map json = new HashMap<>(); json.put("name", "小明"); String req = S.json().toJson(json); String sign = Encrypt.sign(req.getBytes(charset)); HttpPost post = new HttpPost(getUrl(act) + "?__sign=" + sign); StringEntity se = new StringEntity(req,charset); post.setEntity(se); HttpResponse resp = client.execute(post); //以下验证请求是否正确 String line = resp.getStatusLine().toString(); Assert.assertEquals("HTTP/1.1 200 OK", line); HttpEntity resEntity = resp.getEntity(); String ret = EntityUtils.toString(resEntity,charset); Assert.assertEquals("hello 小明,来自admin的问候", ret); } @Test public void base64() throws IOException { String charset = "UTF-8"; HttpClient client = HttpClientBuilder.create().build(); String act = "base64"; HttpPost post = new HttpPost(getUrl(act)); Map json = new HashMap<>(); json.put("echo", "你好!!!"); json.put("names", Arrays.asList("小明", "小张")); String req = Base64.getEncoder().encodeToString(S.json().toJson(json).replace("\"names\"", "names").getBytes(charset)); System.out.println("req:" + req); StringEntity se = new StringEntity(req, charset); post.setEntity(se); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); Assert.assertEquals("HTTP/1.1 200 OK", line); HttpEntity resEntity = resp.getEntity(); String ret = new String(Base64.getMimeDecoder().decode(EntityUtils.toString(resEntity)), charset); Assert.assertEquals("[\"你好!!! 小明\",\"你好!!! 小张\"]", ret); } @Test public void upload() throws IOException { String charset = "UTF-8"; HttpClient client = HttpClientBuilder.create().build(); String act = "upload"; HttpPost post = new HttpPost(getUploadUrl(act)); Map json = new HashMap<>(); json.put("name", "张三"); json.put("age", 23); String req = Base64.getEncoder().encodeToString(S.json().toJson(json).getBytes(charset)); System.out.println("req:" + req); MultipartEntity reqEntity = new MultipartEntity(); reqEntity.addPart("Api", StringBody.create("common", "text/plain", Charset.forName(charset))); reqEntity.addPart("param", StringBody.create(req, "text/plain", Charset.forName(charset))); reqEntity.addPart("img", new FileBody(new File("test.sql"))); post.setEntity(reqEntity); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); Assert.assertEquals("HTTP/1.1 200 OK", line); HttpEntity resEntity = resp.getEntity(); Log.get("upload").info(EntityUtils.toString(resEntity, charset)); } @Test public void db_insert() throws IOException { String charset = "UTF-8"; HttpClient client = HttpClientBuilder.create().build(); String act = "add"; HttpPost post = new HttpPost(getUrl(act)); List list = new ArrayList<>(); for (int i = 0; i < 10; i++) { DemoUser obj = new DemoUser(); obj.setAge(r.nextInt(100)); obj.setName("名字" + r.nextInt()); obj.setId(r.nextLong()); list.add(obj); } Map json = new HashMap<>(); json.put("users", list); StringEntity se = new StringEntity(S.json().toJson(json), charset); post.setEntity(se); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); System.out.println(line); HttpEntity resEntity = resp.getEntity(); String ret = EntityUtils.toString(resEntity, charset); System.out.println(ret); Assert.assertEquals(list.size() + "", ret); } @Test public void db_insert_query() throws IOException { String charset = "UTF-8"; HttpClient client = HttpClientBuilder.create().build(); String act = "addAndGet"; HttpPost post = new HttpPost(getUrl(act)); List list = new ArrayList<>(); for (int i = 0; i < 10; i++) { DemoUser obj = new DemoUser(); obj.setAge(r.nextInt(100)); obj.setName("名字" + r.nextInt()); obj.setId(r.nextLong()); list.add(obj); } Map json = new HashMap<>(); json.put("users", list); StringEntity se = new StringEntity(S.json().toJson(json), charset); post.setEntity(se); HttpResponse resp = client.execute(post); String line = resp.getStatusLine().toString(); System.out.println(line); HttpEntity resEntity = resp.getEntity(); String ret = EntityUtils.toString(resEntity, charset); Log.get("db_insert_query").info("返回结果:" + ret); System.out.println(ret); } } ================================================ FILE: sumk-test/src/test/java/org/test/client/HttpPressTest.java ================================================ /** * Copyright (C) 2016 - 2017 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.test.client; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.junit.Assert; import org.junit.Test; import org.yx.common.util.S; public class HttpPressTest { @Test public void test() throws IOException, InterruptedException { String charset = "utf-8"; HttpClient client = HttpClientBuilder.create().setMaxConnTotal(5000).setMaxConnPerRoute(1000).build(); ExecutorService executor=Executors.newFixedThreadPool(500); HttpPost post = new HttpPost("http://localhost:8080/rest/echo"); Map json = new HashMap<>(); json.put("echo", "你好!!!"); json.put("names", Arrays.asList("小明", "小张")); StringEntity se = new StringEntity(S.json().toJson(json), charset); post.setEntity(se); System.out.println("开始压测,请耐心等待10秒左右。。。"); long begin=System.currentTimeMillis(); int count=100000; AtomicLong totalRT=new AtomicLong(); AtomicLong success=new AtomicLong(); for(int i=0;i{ try { long b2=System.currentTimeMillis(); HttpResponse resp = client.execute(post); HttpEntity resEntity = resp.getEntity(); totalRT.addAndGet(System.currentTimeMillis()-b2); String ret = EntityUtils.toString(resEntity, charset); Assert.assertEquals("[\"你好!!! 小明\",\"你好!!! 小张\"]", ret); success.incrementAndGet(); } catch (Exception e) { e.printStackTrace(); } }); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.DAYS); long time=System.currentTimeMillis()-begin; Assert.assertEquals(count, success.get()); System.out.println(count+"次http请求总耗时:"+time+"ms,平均每秒请求数:"+(count*1000d/time)); System.out.println("平均每个请求耗时:"+totalRT.get()/count+"ms"); } } ================================================ FILE: sumk-test/src/test/java/org/test/client/RpcPressTest.java ================================================ package org.test.client; import java.util.Arrays; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.yx.log.LogLevel; import org.yx.log.Loggers; import org.yx.main.StartConstants; import org.yx.main.SumkServer; import org.yx.rpc.client.LockHolder; import org.yx.rpc.client.Rpc; import org.yx.util.SumkDate; public class RpcPressTest { @Before public void before(){ SumkServer.start(RpcPressTest.class,Arrays.asList(StartConstants.NOHTTP,StartConstants.NOSOA)); Loggers.setDefaultLevel(LogLevel.ERROR);//这个只能修改默认级别的,如果有具体设置了日志级别,它的优先级比这个高 Rpc.init(); } @After public void after(){ Assert.assertEquals(0, LockHolder.lockSize()); System.out.println("锁状态正确!"); } Random r=new Random(); @Test public void test() throws InterruptedException { System.out.println("开始压测,请耐心等待30秒左右。。。"); final int count=50_0000; final int threadCount=50; AtomicInteger failCount=new AtomicInteger(); CountDownLatch down=new CountDownLatch(count); Rpc.call("a.b.repeat", "预热"); char[] cs=new char[1200]; Arrays.fill(cs, 't'); Arrays.fill(cs, 100,200,'好'); cs[5]='啊'; cs[45]='1'; cs[59]='o'; cs[115]='是'; cs[800]='是'; String pre=new String(cs); long begin=System.currentTimeMillis(); AtomicLong totalRT=new AtomicLong(); for(int i=0;i names = Arrays.asList("游夏", "游侠"); String echo = "how are you"; // ret是json格式 String ret = Rpc.call(soaName("echo"), echo, names); System.out.println("result:" + ret); Assert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class)); for (int i = 0; i < 5; i++) { Map map = new HashMap<>(); map.put("echo", echo); map.put("names", names); ret = Rpc.callInMap(soaName("echo"), map); Assert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class)); ret = Rpc.call(soaName("echo"), echo, names); Assert.assertEquals(new EchoAction().echo(echo, names), S.json().fromJson(ret, List.class)); System.out.println("test:" + ret); } } Random r = new Random(); private List create() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { DemoUser obj = new DemoUser(); obj.setAge(r.nextInt(100)); obj.setName("名字" + r.nextInt()); obj.setId(r.nextLong()); list.add(obj); } return list; } @Test public void db_insert() throws IOException { for (int j = 0; j < 5; j++) { List list = create(); String ret; Map map = new HashMap<>(); map.put("users", list); ret = Rpc.callInMap(soaName("add"), map); System.out.println("返回的信息:" + ret); Assert.assertEquals(list.size() + "", ret); list = create(); ret = Rpc.call(soaName("add"), list); System.out.println("返回的信息:" + ret); Assert.assertEquals(list.size() + "", ret); } } @Test public void async() { System.out.println("now:" + System.currentTimeMillis()); List names = Arrays.asList("游夏", "游侠"); String echo = "how are you,"; // ret是json格式 List retList = new ArrayList<>(); List retList2 = new ArrayList<>(); for (int i = 0; i < 5; i++) { Map map = new HashMap<>(); map.put("echo", echo + i); map.put("names", names); retList.add(Rpc.callInMapAsync(soaName("echo"), map)); retList2.add(Rpc.callAsync(soaName("echo"), echo + i, names)); } for (int i = 0; i < 5; i++) { System.out.println(i + " 异步"); Assert.assertEquals(new EchoAction().echo(echo + i, names), S.json().fromJson(retList.get(i).opt(), List.class)); Assert.assertEquals(new EchoAction().echo(echo + i, names), S.json().fromJson(retList2.get(i).opt(), List.class)); } } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/dao/DemoUserDao.java ================================================ /** * Copyright (C) 2016 - 2017 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.test.inner.dao; import java.text.ParseException; import org.test.inner.po.DemoUser; public interface DemoUserDao { Long insert(DemoUser obj); // 更新部分字段 void updatePart(DemoUser obj); // 更新全部字段 void fullUpate(long id); void softDelete(long id); DemoUser query(long id); void select() throws ParseException; } ================================================ FILE: sumk-test/src/test/java/org/test/inner/dao/DemoUserDaoImpl.java ================================================ package org.test.inner.dao; import java.text.ParseException; import java.util.Date; import java.util.List; import java.util.Random; import org.junit.Assert; import org.test.inner.po.DemoUser; import org.yx.annotation.Bean; import org.yx.annotation.db.Box; import org.yx.common.util.SBuilder; import org.yx.db.DB; import org.yx.db.sql.Select; import org.yx.db.visit.MapResultHandler; @Bean public class DemoUserDaoImpl implements DemoUserDao { public Random r = new Random(); @Override @Box public Long insert(DemoUser obj) { if (obj == null) { obj = new DemoUser(); obj.setAge(r.nextInt(100)); obj.setName("名字" + r.nextInt()); obj.setLastUpdate(new Date()); } System.out.println("插入:" + DB.insert(obj).execute() + "条,id=" + obj.getId()); return obj.getId(); } // 更新部分字段 @Override @Box public void updatePart(DemoUser obj) { obj.setName("名字改为:" + r.nextInt()); DB.update(obj).execute(); } // 更新全部字段 @Override @Box public void fullUpate(long id) { DemoUser obj = new DemoUser(); obj.setId(id); obj.setName("全部更新,除名字外都清空"); DB.update(obj).fullUpdate(true).execute(); } @Override @Box public void softDelete(long id) { DemoUser obj = new DemoUser(); obj.setId(id); DB.delete(obj).execute(); } @Override @Box public DemoUser query(long id) { return DB.select().tableClass(DemoUser.class).byDatabaseId(id).queryOne(); } @Override @Box public void select() throws ParseException { DemoUser obj = new DemoUser(); obj.setAge(12); obj.setName("kkk"); List list; System.out.println("查询中用到的所有字段名,都是java的字段名,而不是数据库的字段名"); System.out.println("查询name=kkk and age=12"); list = DB.select(obj).queryList(); // 查询name=kkk Assert.assertEquals(list.size(), DB.select(obj).count()); System.out.println("用map做条件,查询(id=10000 and age =16) or (id=20000)的记录。用map做条件的时候,key的大小写不敏感,但值类型要跟pojo类定义的一致"); Select select = DB.select().tableClass(DemoUser.class) .addEqual(SBuilder.map("id", 10000).put("age", 16).toMap()).addEqual(SBuilder.map("id", 20000).toMap()); list = select.queryList(); Assert.assertEquals(list.size(), select.count()); System.out.println("返回结果是List的例子。查询lastupdate<当前时间的记录,按lastupdate升序排列,并且limit 10,10(相当于每页10条的第二页数据)"); list = DB.select().tableClass(DemoUser.class).lessThan("lastupdate", new Date()).orderByAsc("lastupdate") .offset(10).limit(10).resultHandler(MapResultHandler.handler).queryList(); System.out.println("map list:" + list); System.out.println( "相当于select id demouser where (name=kkk and age=12) or (name=第1个 and age=1) order by lastupdate,age desc limit 0,10"); DB.select(obj).selectColumns("id").addEqual(SBuilder.map("name", "第1个").put("AGE", 1).toMap()) .orderByAsc("lastupdate").orderByDesc("age").limit(10).queryList(); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/dao/DemoUserMybatisDao.java ================================================ package org.test.inner.dao; import java.text.ParseException; import java.util.Date; import java.util.List; import java.util.Random; import org.test.inner.po.DemoUser; import org.yx.annotation.db.Box; import org.yx.common.util.SBuilder; import org.yx.db.DB; import org.yx.db.visit.MapResultHandler; /** * mybatis类型的数据库操作,在本工程中没有实际用处,只是给大家做参考 */ public class DemoUserMybatisDao { public Random r = new Random(); @Box public Long insert(DemoUser obj) { if (obj == null) { obj = new DemoUser(); obj.setAge(r.nextInt(100)); obj.setName("名字" + r.nextInt()); obj.setLastUpdate(new Date()); } System.out.println("插入:" + DB.insert(obj).execute() + "条,id=" + obj.getId()); return obj.getId(); } // 更新部分字段 @Box public void updatePart(DemoUser obj) { obj.setName("名字改为:" + r.nextInt()); DB.update(obj).execute(); } // 更新全部字段 @Box public void fullUpate(long id) { DemoUser obj = new DemoUser(); obj.setId(id); obj.setName("全部更新,除名字外都清空"); DB.update(obj).fullUpdate(true).execute(); } @Box public void softDelete(long id) { DemoUser obj = new DemoUser(); obj.setId(id); DB.delete(obj).execute(); } @Box public DemoUser query(long id) { return DB.select().tableClass(DemoUser.class).byDatabaseId(id).queryOne(); } @Box public void select() throws ParseException { DemoUser obj = new DemoUser(); obj.setAge(12); obj.setName("kkk"); List list; System.out.println("查询中用到的所有字段名,都是java的字段名,而不是数据库的字段名"); System.out.println("查询name=kkk and age=12"); list = DB.select(obj).queryList(); // 查询name=kkk System.out.println("用map做条件,查询(id=10000 and age =16) or (id=20000)的记录。用map做条件的时候,key的大小写不敏感,但值类型要跟pojo类定义的一致"); list = DB.select().tableClass(DemoUser.class).addEqual(SBuilder.map("id", 10000).put("age", 16).toMap()) .addEqual(SBuilder.map("id", 20000).toMap()).queryList(); System.out.println("返回结果是List的例子。查询lastupdate<当前时间的记录,按lastupdate升序排列,并且limit 10,10(相当于每页10条的第二页数据)"); list = DB.select().tableClass(DemoUser.class).lessThan("lastupdate", new Date()).orderByAsc("lastupdate") .offset(10).limit(10).resultHandler(MapResultHandler.handler).queryList(); System.out.println("map list:" + list); System.out.println( "相当于select id demouser where (name=kkk and age=12) or (name=第1个 and age=1) order by lastupdate,age desc limit 0,10"); DB.select(obj).selectColumns("id").addEqual(SBuilder.map("name", "第1个").put("AGE", 1).toMap()) .orderByAsc("lastupdate").orderByDesc("age").limit(10).queryList(); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/dao/LocalSqlDao.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.test.inner.dao; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.yx.annotation.Bean; import org.yx.annotation.db.Box; import org.yx.common.util.SBuilder; import org.yx.db.SDB; @Bean public class LocalSqlDao { @Box public int insertBatch(Map map){ return SDB.execute("demo.insertBatch", map); } @Box public int insert(String name,long id,int age){ Map map=SBuilder.map("id",id).put("name", name).put("age", age).toMap(); return SDB.execute("demo.insert", map); } @Box public int update(String name,long id,int age,Date lastUpdate){ Map map=SBuilder.map("name",name) .put("id", id) .put("age", age) .put("lastUpdate", lastUpdate) .toMap(); return SDB.execute("demo.update", map); } @Box public Map select(Long id){ return SDB.queryOne("demo.select",SBuilder.map("id", id).put("table", "demo_user").toMap()); } @Box public Map select(Map param){ Map map=new HashMap<>(param); map.put("table", "demo_user"); return SDB.queryOne("demo.select",map); } @Box public List> selectByIds(Map param){ return SDB.list("demo.selectByIds",param); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/dao/MultikeyDao.java ================================================ package org.test.inner.dao; import java.util.Random; import org.test.inner.po.Multikey; import org.yx.annotation.Bean; import org.yx.annotation.db.Box; import org.yx.db.DB; import org.yx.util.UUIDSeed; @Bean public class MultikeyDao { Random r = new Random(); @Box public Multikey insert(Multikey obj) { if (obj == null) { obj = create("名字" + r.nextInt(), r.nextInt(100)); } System.out.println("插入:" + DB.insert(obj).execute() + "条,id1=" + obj.getId1()); return obj; } @Box public void updatePart(Multikey obj) { obj.setName("名字改为:" + r.nextInt()); DB.update(obj).execute(); } @Box public void fullUpate(String id1, String id2) { Multikey obj = new Multikey(); obj.setId1(id1).setId2(id2); obj.setName("全部更新,除名字外都清空"); DB.update(obj).fullUpdate(true).execute(); } @Box public int delete(Multikey obj) { return DB.delete(obj).execute(); } @Box public Multikey query(String id1, String id2) { return DB.select(new Multikey().setId1(id1).setId2(id2)).queryOne(); } private Multikey create(String name, int age) { Multikey obj = new Multikey(); obj.setId1(UUIDSeed.seq()); obj.setId2(UUIDSeed.seq()); obj.setAge(age); obj.setName(name); return obj; } @Box public void incrAge(Multikey obj, int age) { DB.update(obj).incrNum("age", age).execute(); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/po/DemoUser.java ================================================ package org.test.inner.po; import java.util.Date; import org.yx.annotation.db.Column; import org.yx.annotation.db.SoftDelete; import org.yx.annotation.db.Table; import org.yx.db.enums.ColumnType; import org.yx.util.SumkDate; @Table @SoftDelete(value = "enable", type = Byte.class) public class DemoUser { @Column(type = ColumnType.ID_BOTH) private Long id; private String name; private Integer age; private Date lastUpdate; public Date getLastUpdate() { return lastUpdate; } public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "DemoUser [id=" + id + ", name=" + name + ", age=" + age + ", lastUpdate=" + (lastUpdate==null ? "null" : SumkDate.of(lastUpdate)).toString() + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((age == null) ? 0 : age.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DemoUser other = (DemoUser) obj; if (age == null) { if (other.age != null) return false; } else if (!age.equals(other.age)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/po/Multikey.java ================================================ package org.test.inner.po; import org.yx.annotation.db.Column; import org.yx.annotation.db.Table; import org.yx.db.enums.ColumnType; @Table public class Multikey { @Column(type = ColumnType.ID_BOTH, order = 1) private String id1; @Column(type = ColumnType.ID_BOTH, order = 2) private String id2; private String name; private Integer age; public String getId1() { return id1; } public Multikey setId1(String id1) { this.id1 = id1; return this; } public String getId2() { return id2; } public Multikey setId2(String id2) { this.id2 = id2; return this; } public String getName() { return name; } public Multikey setName(String name) { this.name = name; return this; } public Integer getAge() { return age; } public Multikey setAge(Integer age) { this.age = age; return this; } @Override public String toString() { return "Multikey [id1=" + id1 + ", id2=" + id2 + ", name=" + name + ", age=" + age + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((age == null) ? 0 : age.hashCode()); result = prime * result + ((id1 == null) ? 0 : id1.hashCode()); result = prime * result + ((id2 == null) ? 0 : id2.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Multikey other = (Multikey) obj; if (age == null) { if (other.age != null) return false; } else if (!age.equals(other.age)) return false; if (id1 == null) { if (other.id1 != null) return false; } else if (!id1.equals(other.id1)) return false; if (id2 == null) { if (other.id2 != null) return false; } else if (!id2.equals(other.id2)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/soa/demo/EchoAction.java ================================================ package org.test.inner.soa.demo; import java.util.ArrayList; import java.util.List; import org.yx.annotation.Bean; import org.yx.annotation.rpc.Soa; @Bean public class EchoAction { @Soa("a.b.repeat") public String repeat(String s){ return s; } @Soa public List echo(String echo, List names) { List list = new ArrayList(); for (String name : names) { list.add(echo + " " + name); } return list; } @Soa public String hi() { return "hello"; } @Soa public void print() { System.out.println("print"); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/soa/server/SOAServer.java ================================================ package org.test.inner.soa.server; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.yx.util.UUIDSeed; public class SOAServer { private static ZooKeeperServer zk; private static NIOServerCnxnFactory nioFactory; public static void startZKServer() throws IOException, InterruptedException{ startZKServer(500,2000); } public static void stopZKServer(){ if(zk!=null){ zk.shutdown(); } } public static void startZKServer(int tickTime, int minSessionTimeout) throws IOException, InterruptedException{ String temp=System.getProperty("java.io.tmpdir"); File f=new File(temp,UUIDSeed.seq()); f.mkdir(); zk = new ZooKeeperServer(f, f, tickTime); zk.setMinSessionTimeout(minSessionTimeout); nioFactory = new NIOServerCnxnFactory(); int maxClientConnections = 0; // 0 means unlimited nioFactory.configure(new InetSocketAddress("127.0.0.1",2181), maxClientConnections); nioFactory.startup(zk); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/web/client/Encrypt.java ================================================ /** * Copyright (C) 2016 - 2017 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.test.inner.web.client; import java.security.MessageDigest; import org.yx.common.util.S; /** * 客户端使用这个类进行加解密、签名 */ public class Encrypt { public static byte[] encrypt(byte[] contentBytes, byte[] key) throws Exception { return S.cipher().encrypt(contentBytes, key); } public static byte[] decrypt(byte[] contentBytes, byte[] key) throws Exception { return S.cipher().decrypt(contentBytes, key); } /** * 对提交数据的明文进行签名 * * @param data * @return * @throws Exception */ public static String sign(byte[] data) throws Exception { return parseByte2HexStr(encryptByte(data)); } public static byte[] encryptByte(byte[] data) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(data); return md.digest(); } private static String parseByte2HexStr(byte buf[]) { StringBuilder sb = new StringBuilder(32); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex); } return sb.toString().toLowerCase(); } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/web/demo/AesTestServer.java ================================================ package org.test.inner.web.demo; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.yx.annotation.Bean; import org.yx.annotation.http.Web; import org.yx.http.MessageType; import org.yx.http.WebUtil; @Bean public class AesTestServer { @Web(value = "aes_base64", requestType = MessageType.ENCRYPT_BASE64, responseType = MessageType.ENCRYPT_BASE64) public List aes_base64(String echo, List names) { Assert.assertEquals("admin", WebUtil.getUserObject(DemoSessionObject.class).getUserId()); List list = new ArrayList<>(); for (String name : names) { list.add(echo + " " + name); } return list; } @Web(requestType = MessageType.ENCRYPT_BASE64, responseType = MessageType.ENCRYPT_BASE64, sign = true) public String aes_sign(String name) { return "hello " + name; } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/web/demo/DBDemo.java ================================================ package org.test.inner.web.demo; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.test.inner.po.DemoUser; import org.yx.annotation.Bean; import org.yx.annotation.db.Box; import org.yx.annotation.http.Web; import org.yx.annotation.rpc.Soa; import org.yx.common.util.SeqUtil; import org.yx.db.DB; import org.yx.db.enums.DBType; import org.yx.db.sql.Insert; import org.yx.exception.BizException; /* * 这个文件用来演示DB操作,其中@Box相当于Spring的@Transactional,用来开启数据库事务。 * 在Sumk中它是必须的,而且也只需要这个注解。 * 除此之外,也演示了将@Box、@Soa和@Web混合使用,实际开发中可能会将Controller和Service分开 */ @Bean public class DBDemo { @Box public long insert(long id, boolean success) { if (id == 0) { id = SeqUtil.next(); } DemoUser user = new DemoUser(); user.setAge(2343); user.setId(id); user.setName(success ? "成功插入" : "失败记录"); DB.insert(user).execute(); if (!success) { BizException.throwException(3242, "故意出错"); } return id; } @Web @Soa @Box(dbType = DBType.WRITE) public int add(List users) { //这个是批处理方式 Insert insert=DB.insert(DemoUser.class); for (DemoUser user : users) { insert.insert(user); } return insert.execute(); } @Web @Box(dbType = DBType.WRITE) // 实际开发中,一般不需要dbType=DBType.WRITE。只有在插入后要立即查询的情况下,才需要加入这行 public List addAndGet(List users) { for (DemoUser user : users) { DB.insert(user).execute(); } List list = new ArrayList<>(); for (DemoUser user : users) { DemoUser user2 = DB.select().tableClass(DemoUser.class).byDatabaseId(user.getId()).queryOne(); Assert.assertEquals(DB.select().tableClass(DemoUser.class).byDatabaseId(user.getId()).queryOne(), user2); list.add(user2); } return list; } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/web/demo/DemoSessionObject.java ================================================ /** * Copyright (C) 2016 - 2017 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.test.inner.web.demo; import org.yx.http.user.SessionObject; public class DemoSessionObject extends SessionObject { } ================================================ FILE: sumk-test/src/test/java/org/test/inner/web/demo/MyLoginServlet.java ================================================ package org.test.inner.web.demo; import javax.servlet.http.HttpServletRequest; import org.test.inner.po.DemoUser; import org.yx.annotation.Bean; import org.yx.annotation.db.Box; import org.yx.common.util.SeqUtil; import org.yx.db.DB; import org.yx.http.user.AbstractLoginServlet; import org.yx.http.user.LoginObject; @Bean public class MyLoginServlet extends AbstractLoginServlet { @Override @Box protected LoginObject login(String token, String user, HttpServletRequest req) { String password = req.getParameter("password"); String validCode = req.getParameter("code"); System.out.println("login的log:" + DB.select().tableClass(DemoUser.class).byDatabaseId(log()).queryOne()); if (!"9999".equals(validCode)) { return LoginObject.fail("验证码错误"); } if ("admin".equals(user) && "123456".equals(password)) { DemoSessionObject so = new DemoSessionObject(); so.setUserId("admin"); return LoginObject.success(null, so); } return LoginObject.fail("用户名或密码错误"); } public long log() { long id = SeqUtil.next(); DemoUser user = new DemoUser(); user.setAge(2323443); user.setId(id); user.setName("登陆"); DB.insert(user).execute(); return id; } } ================================================ FILE: sumk-test/src/test/java/org/test/inner/web/demo/PlainServer.java ================================================ package org.test.inner.web.demo; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.yx.annotation.Bean; import org.yx.annotation.Param; import org.yx.annotation.http.Upload; import org.yx.annotation.http.Web; import org.yx.exception.BizException; import org.yx.http.MessageType; import org.yx.http.WebUtil; import org.yx.http.handler.MultipartItem; import org.yx.util.IOUtil; @Bean public class PlainServer { @Web(value = "echo") public List echo(String echo, List names) { List list = new ArrayList<>(); for (String name : names) { list.add(echo + " " + name); } return list; } @Web(value = "base64", requestType = MessageType.BASE64, responseType = MessageType.BASE64) public List base64(@Param(max = 20) String echo, List names) { List list = new ArrayList<>(); for (String name : names) { list.add(echo + " " + name); } return list; } @Web(value = "upload", requestType = MessageType.BASE64) @Upload public String upload(String name, @Param(required = true) Integer age) throws FileNotFoundException, IOException { Assert.assertEquals("张三", name); Assert.assertEquals(Integer.valueOf(23), age); List files=WebUtil.getMultiParts(); Assert.assertEquals(2, files.size()); MultipartItem f=WebUtil.getPart("img"); Assert.assertEquals("test.sql", f.getSubmittedFileName()); byte[] data=IOUtil.readAllBytes(f.getInputStream(),false); byte[] exp=Files.readAllBytes(new File("test.sql").toPath()); Assert.assertArrayEquals(exp, data); return "姓名:"+name+",年龄:"+age; } //本接口要等陆后才能用 @Web(value = "plain_sign", sign = true,requireLogin=true) public String plain_sign(String name) { return "hello " + name+",来自"+WebUtil.getUserObject(DemoSessionObject.class).getUserId()+"的问候"; } @Web(requestType = MessageType.ENCRYPT_BASE64, responseType = MessageType.ENCRYPT_BASE64) public String bizError() { System.out.println("req:" + WebUtil.getHttpRequest()); BizException.throwException(12345, "业务异常"); return ""; } } ================================================ FILE: sumk-test/src/test/java/org/test/orm/BaseOrmTest.java ================================================ package org.test.orm; import java.util.Arrays; import org.junit.BeforeClass; import org.test.Main; import org.yx.log.LogLevel; import org.yx.log.Loggers; import org.yx.main.SumkServer; /** * 外键测试 * * @author 游夏 * */ public class BaseOrmTest { @BeforeClass public static void beforeClass() { SumkServer.start(Main.class,Arrays.asList("nosoa", "nohttp")); Loggers.setDefaultLevel(LogLevel.DEBUG); } } ================================================ FILE: sumk-test/src/test/java/org/test/orm/MultiPrimaryTest.java ================================================ package org.test.orm; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.test.inner.dao.MultikeyDao; import org.test.inner.po.Multikey; import org.yx.bean.IOC; public class MultiPrimaryTest extends BaseOrmTest{ private MultikeyDao dao; @Before public void before() { dao = IOC.get(MultikeyDao.class); } @Test public void crud() { Multikey obj = dao.insert(null); Assert.assertEquals(obj, dao.query(obj.getId1(), obj.getId2())); dao.fullUpate(obj.getId1(), obj.getId2()); Multikey real = new Multikey(); real.setId1(obj.getId1()).setId2(obj.getId2()); real.setName("全部更新,除名字外都清空"); Assert.assertEquals(real, dao.query(obj.getId1(), obj.getId2())); Assert.assertEquals(1, dao.delete(real)); Assert.assertNull(dao.query(obj.getId1(), obj.getId2())); } } ================================================ FILE: sumk-test/src/test/java/org/test/orm/SinglePrimaryTest.java ================================================ package org.test.orm; import java.io.InputStream; import java.sql.Timestamp; import java.text.ParseException; import java.util.Random; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.test.inner.dao.DemoUserDao; import org.test.inner.po.DemoUser; import org.yx.bean.IOC; import org.yx.main.SumkServer; import org.yx.util.IOUtil; //单主键的测试 public class SinglePrimaryTest extends BaseOrmTest{ private DemoUserDao dao; @Before public void before() { dao = IOC.get(DemoUserDao.class); } @Test public void crud() { DemoUser obj = new DemoUser(); obj.setAge(new Random().nextInt(100)); obj.setName("名字" + new Random().nextInt()); obj.setLastUpdate(new Timestamp(System.currentTimeMillis() / 1000 * 1000));// timestamp类型的字段,如果是mysql数据库。用Timestamp或Date类都行,但oracle只能用Timestamp类型 dao.insert(obj); Assert.assertEquals(obj, dao.query(obj.getId())); dao.fullUpate(obj.getId()); DemoUser real = new DemoUser(); real.setId(obj.getId()); real.setName("全部更新,除名字外都清空"); Assert.assertEquals(real, dao.query(obj.getId())); dao.softDelete(obj.getId()); Assert.assertNull(dao.query(obj.getId())); } @Test public void select() throws ParseException { dao.select(); } @Test public void lockFile() throws Exception { InputStream in=SumkServer.class.getClassLoader().getResourceAsStream("META-INF/lua_del"); byte[] bs=IOUtil.readAllBytes(in, true); Assert.assertEquals(95, bs.length); String script=new String(bs); Assert.assertFalse(script.contains("\r")); } } ================================================ FILE: sumk-test/src/test/java/org/test/orm/SqlTest.java ================================================ /** * Copyright (C) 2016 - 2030 youtongluan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.test.orm; import java.io.File; import java.net.URL; import java.nio.file.Files; import java.sql.Timestamp; import java.util.Date; import java.util.Map; import java.util.Random; import org.junit.Assert; import org.junit.Test; import org.test.inner.dao.LocalSqlDao; import org.yx.base.date.TimeUtil; import org.yx.bean.IOC; import org.yx.conf.AppInfo; import org.yx.conf.Const; /* * useOldAliasMetadataBehavior=true表示启用别名,额不是数据库定义时的名字,注意连接里这个参数对测试用例的影响 */ public class SqlTest extends BaseOrmTest{ @Test public void test() { LocalSqlDao dao=IOC.get(LocalSqlDao.class); String name="sdfdsgf"; long id=new Random().nextLong(); int age=103; int ret=dao.insert(name, id, age); Assert.assertEquals(1, ret); Map map=dao.select(id); System.out.println(map); Assert.assertEquals(name, map.get("name")); Assert.assertEquals(id, map.get("id")); Assert.assertEquals(age, map.get("age")); Map map2=dao.select(id); Assert.assertEquals(map, map2); //mysql的Timestamp只支持到秒,毫秒会被四舍五入 Date lastUpdate=new Date(System.currentTimeMillis()/1000*1000); ret=dao.update(name+"_1", id, age+1, lastUpdate); Assert.assertEquals(1, ret); map=dao.select(id); System.out.println(map); Assert.assertEquals(name+"_1", map.get("name")); Assert.assertEquals(id, map.get("id")); Assert.assertEquals(age+1, map.get("age")); System.out.println(map.get("lastUpdate").getClass()); Assert.assertEquals(lastUpdate, TimeUtil.toType(map.get("lastUpdate"), Timestamp.class, true)); } @Test public void versionTest() throws Exception { URL url=this.getClass().getResource("SqlTest.class"); File f=new File(url.toURI()); while(!f.getName().equals("target")) { f=f.getParentFile(); } f=f.getParentFile(); f=new File(f,"pom.xml"); System.out.println(f); System.out.println(Const.sumkVersion()); String pom=new String(Files.readAllBytes(f.toPath()),AppInfo.UTF8); Assert.assertEquals(pom.indexOf(""),pom.indexOf(""+Const.sumkVersion()+"")); } } ================================================ FILE: sumk-test/src/test/java/org/test/token/MapedSqlTokenParserTest.java ================================================ package org.test.token; import org.junit.Assert; import org.junit.Test; import org.yx.db.sql.MapedSql; import org.yx.db.sql.token.MapedSqlTokenParser; import java.util.ArrayList; public class MapedSqlTokenParserTest { @Test public void testParse() { final MapedSql parsed = new MapedSqlTokenParser("<", ">", null).parse(""); Assert.assertNotNull(parsed); Assert.assertEquals("", parsed.getSql()); Assert.assertNull(parsed.getEvent()); Assert.assertEquals(new ArrayList<>(), parsed.getParamters()); final MapedSql parsed2 = new MapedSqlTokenParser("<", ">", null).parse(null); Assert.assertNotNull(parsed2); Assert.assertEquals("", parsed2.getSql()); Assert.assertNull(parsed2.getEvent()); Assert.assertEquals(new ArrayList<>(), parsed2.getParamters()); final MapedSql parsed3 = new MapedSqlTokenParser("<", ">", null).parse("text"); Assert.assertNotNull(parsed3); Assert.assertEquals("text", parsed3.getSql()); Assert.assertNull(parsed3.getEvent()); Assert.assertEquals(new ArrayList<>(), parsed3.getParamters()); } } ================================================ FILE: sumk-test/src/test/java/org/test/token/StringTokenParserTest.java ================================================ package org.test.token; import org.junit.Assert; import org.junit.Test; import org.yx.db.sql.token.StringTokenParser; public class StringTokenParserTest { @Test public void testParse() { final StringTokenParser objectUnderTest = new StringTokenParser("<", ">", null); Assert.assertEquals("", objectUnderTest.parse("")); Assert.assertEquals("", objectUnderTest.parse(null)); Assert.assertEquals("text", objectUnderTest.parse("text")); } } ================================================ FILE: sumk-test/src/test/resources/app.properties ================================================ #推荐使用UTF-8编码 #微服务中的zookeeper地址 sumk.zkurl=127.0.0.1:2181 #used in RPC sumk.rpc.port=10086 #used in HTTP。zookeeper 3.9.x会默认占用8080,可以在zoo.cfg里配置admin.serverPort来修改zk的端口 sumk.http.port=8080 #sumk.ioc=org.test #redis配置 #s.redis.default=127.0.0.1 #数据库写库的配置 s.db.sumk.1.type=wr s.db.sumk.1.url=jdbc:mysql://120.55.49.121:3306/sumk?characterEncoding=utf-8&useOldAliasMetadataBehavior=true&useSSL=false s.db.sumk.1.username=sumk s.db.sumk.1.password=sumk123456 #数据库读库的配置 s.db.sumk.2.type=read s.db.sumk.2.url=jdbc:mysql://120.55.49.121:3306/sumk?characterEncoding=utf-8&useOldAliasMetadataBehavior=true&useSSL=false s.db.sumk.2.username=read s.db.sumk.2.password=sumk123456 ================================================ FILE: sumk-test/src/test/resources/sql/test.xml ================================================ select * from ${table} where 1=1 and name like #{name} age=age id in #{@} limit 10 select * from ${table} 1234 ]]> limit 1 select * from ${table} where 1=1 and name like #{name} age=age limit 10 INSERT INTO demo_user ( name , id , age, enable ) VALUES (#{name},#{id},#{age},1) ================================================ FILE: sumk-test/src/test/resources/sql/user/demo.xml ================================================ insert into demo_user (id,name,age,last_update) values ( #{id},#{name},#{age},#{lastUpdate} ) insert demo_user (name,age,last_update,id) values (#{item.name},#{item.age},#{item.lastUpdate},#{item.id}) UPDATE demo_user SET name = #{name} , age = #{age} , last_update=#{lastUpdate} WHERE id = #{id} select id,name,age,last_update as lastUpdate from ${table} where 1=1 and id = #{id} and name= #{name} and age= #{age} and last_update= #{lastUpdate} select id,name,age,last_update as lastUpdate from demo_user where id in #{id} ================================================ FILE: sumk-test/test.sql ================================================ create database sumk; use sumk; SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `demo_user`; CREATE TABLE `demo_user` ( `id` bigint(20) NOT NULL, `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, `last_update` timestamp NULL DEFAULT NULL, `enable` tinyint(4) DEFAULT '1' COMMENT '1表示有效记录,0表示记录已被删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `generate`; CREATE TABLE `generate` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `multikey`; CREATE TABLE `multikey` ( `id1` varchar(255) NOT NULL COMMENT '联合主键1', `id2` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL COMMENT '联合主键2', `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id1`,`id2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `odd`; CREATE TABLE `odd` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `data` mediumblob, `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2208669043496333243 DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `school_fuzhou`; CREATE TABLE `school_fuzhou` ( `id` bigint(20) NOT NULL, `name` varchar(255) DEFAULT NULL, `lastUpdate` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `test_table`; CREATE TABLE `test_table` ( `id` bigint(20) NOT NULL COMMENT 'Long 类型,第1列', `userid` varchar(255) NOT NULL COMMENT 'String 类型,联合索引1,第2列', `enable` bit(1) NOT NULL COMMENT 'boolean 类型,联合索引2,第3列', `height` smallint(6) DEFAULT NULL COMMENT 'Short 类型,第4列', `age` tinyint(4) DEFAULT NULL COMMENT 'byte 类型,第5列', `f` decimal(12,3) DEFAULT NULL COMMENT 'float 类型,第6列', `d` decimal(20,5) DEFAULT NULL COMMENT 'double 类型,第7列', `valid` bit(1) DEFAULT b'1' COMMENT '1表示有效记录,0表示记录已被删除', PRIMARY KEY (`id`), KEY `testtable_index1` (`userid`,`enable`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用来测试表格自动生成'; DROP TABLE IF EXISTS `timedemo`; CREATE TABLE `timedemo` ( `id` bigint(20) NOT NULL, `name` varchar(255) DEFAULT NULL, `lastUpdate` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `user_detail`; CREATE TABLE `user_detail` ( `id` bigint(20) NOT NULL, `user_id` bigint(20) NOT NULL, `addr` varchar(255) DEFAULT NULL, `height` int(11) DEFAULT NULL, `valid` char(1) DEFAULT '1' COMMENT '1表示有效记录,0表示记录已被删除', PRIMARY KEY (`id`), KEY `user_detail_index1` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;