diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 6f39ba4f09247bdb861fb95641a54252783ea2c7..4ed3093eb2aa5f37012c3df6a4e305e8d26e48eb 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -1,235 +1,235 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.mq; - -import com.jfinal.kit.LogKit; -import com.jfinal.log.Log; -import io.jboot.Jboot; -import io.jboot.components.serializer.JbootSerializer; -import io.jboot.utils.NamedThreadFactory; -import io.jboot.utils.StrUtil; - -import java.util.*; -import java.util.concurrent.*; - - -public abstract class JbootmqBase implements Jbootmq { - - private static final Log LOG = Log.getLog(JbootmqBase.class); - - protected final JbootmqConfig config; - - private List globalListeners = new CopyOnWriteArrayList<>(); - private Map> channelListeners = new ConcurrentHashMap<>(); - - protected Set channels = new HashSet<>(); - protected Set syncRecevieMessageChannels = new HashSet<>(); - protected JbootSerializer serializer; - - - private ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); - - - public JbootmqBase(JbootmqConfig config) { - this.config = config; - String channelString = config.getChannel(); - if (StrUtil.isBlank(channelString)) { - return; - } - - this.channels.addAll(StrUtil.splitToSet(channelString, ",")); - - if (StrUtil.isNotBlank(config.getSyncRecevieMessageChannel())) { - this.syncRecevieMessageChannels.addAll(StrUtil.splitToSet(config.getSyncRecevieMessageChannel(), ",")); - } - } - - - @Override - public void addMessageListener(JbootmqMessageListener listener) { - globalListeners.add(listener); - } - - - @Override - public void addMessageListener(JbootmqMessageListener listener, String forChannel) { - String[] forChannels = forChannel.split(","); - for (String channel : forChannels) { - if (StrUtil.isNotBlank(channel)) { - addChannelListener(channel.trim(), listener); - } - } - } - - private synchronized void addChannelListener(String channel, JbootmqMessageListener listener) { - List listeners = channelListeners.get(channel); - if (listeners == null) { - listeners = new CopyOnWriteArrayList<>(); - channelListeners.put(channel, listeners); - } - listeners.add(listener); - channels.add(channel); - } - - - @Override - public void removeListener(JbootmqMessageListener listener) { - globalListeners.remove(listener); - for (List listeners : channelListeners.values()) { - listeners.remove(listener); - } - } - - @Override - public void removeAllListeners() { - globalListeners.clear(); - channelListeners.forEach((s, list) -> list.clear()); - channelListeners.clear(); - } - - - @Override - public Collection getGlobalListeners() { - return globalListeners; - } - - - @Override - public Collection getListenersByChannel(String channel) { - return channelListeners.get(channel); - } - - public void notifyListeners(String channel, Object message, MessageContext context) { - - boolean globalResult = notifyListeners(channel, message, context, globalListeners); - boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); - - if (!globalResult && !channelResult) { - LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + - channel + " message:" + message); - } - } - - - protected boolean notifyListeners(String channel, Object message, MessageContext context, Collection listeners) { - if (listeners == null || listeners.size() == 0) { - return false; - } - - if (syncRecevieMessageChannels.contains(channel)) { - for (JbootmqMessageListener listener : listeners) { - try { - listener.onMessage(channel, message, context); - } catch (Throwable ex) { - LOG.warn("listener[" + listener.getClass().getName() + "] execute mq message is error. channel:" + - channel + " message:" + message); - } - } - } else { - for (JbootmqMessageListener listener : listeners) { - threadPool.execute(() -> { - listener.onMessage(channel, message, context); - }); - } - } - - return true; - } - - - public JbootSerializer getSerializer() { - if (serializer == null) { - serializer = StrUtil.isNotBlank(config.getSerializer()) - ? Jboot.getSerializer(config.getSerializer()) - : Jboot.getSerializer(); - } - return serializer; - } - - - protected boolean isStarted = false; - - @Override - public boolean startListening() { - if (isStarted) { - return true; - } - - if (channels == null || channels.isEmpty()) { - LogKit.warn("Jboot MQ started fail. because it's channels is empty, please config channels. " + - "MQ name: {}, type:{}", config.getName(), config.getType()); - return false; - } - - try { - isStarted = true; - onStartListening(); - } catch (Exception ex) { - LogKit.error("Jboot MQ start fail!", ex); - isStarted = false; - return false; - } - - return true; - } - - - @Override - public boolean stopListening() { - if (!isStarted) { - return true; - } - - try { - isStarted = false; - onStopListening(); - } catch (Exception ex) { - LogKit.error("Jboot MQ stop fail!", ex); - isStarted = true; - return false; - } - - return true; - } - - public boolean isStarted() { - return isStarted; - } - - protected abstract void onStartListening(); - - protected abstract void onStopListening(); - - - @Override - public JbootmqConfig getConfig() { - return config; - } - - public void setSerializer(JbootSerializer serializer) { - this.serializer = serializer; - } - - public ExecutorService getThreadPool() { - return threadPool; - } - - public void setThreadPool(ExecutorService threadPool) { - this.threadPool = threadPool; - } -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.mq; + +import com.jfinal.kit.LogKit; +import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.components.serializer.JbootSerializer; +import io.jboot.utils.NamedThreadFactory; +import io.jboot.utils.StrUtil; + +import java.util.*; +import java.util.concurrent.*; + + +public abstract class JbootmqBase implements Jbootmq { + + private static final Log LOG = Log.getLog(JbootmqBase.class); + + protected final JbootmqConfig config; + + private List globalListeners = new CopyOnWriteArrayList<>(); + private Map> channelListeners = new ConcurrentHashMap<>(); + + protected Set channels = new HashSet<>(); + protected Set syncRecevieMessageChannels = new HashSet<>(); + protected JbootSerializer serializer; + + + private ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); + + + public JbootmqBase(JbootmqConfig config) { + this.config = config; + String channelString = config.getChannel(); + if (StrUtil.isBlank(channelString)) { + return; + } + + this.channels.addAll(StrUtil.splitToSet(channelString, ",")); + + if (StrUtil.isNotBlank(config.getSyncRecevieMessageChannel())) { + this.syncRecevieMessageChannels.addAll(StrUtil.splitToSet(config.getSyncRecevieMessageChannel(), ",")); + } + } + + + @Override + public void addMessageListener(JbootmqMessageListener listener) { + globalListeners.add(listener); + } + + + @Override + public void addMessageListener(JbootmqMessageListener listener, String forChannel) { + String[] forChannels = forChannel.split(","); + for (String channel : forChannels) { + if (StrUtil.isNotBlank(channel)) { + addChannelListener(channel.trim(), listener); + } + } + } + + public final synchronized void addChannelListener(String channel, JbootmqMessageListener listener) { + List listeners = channelListeners.get(channel); + if (listeners == null) { + listeners = new CopyOnWriteArrayList<>(); + channelListeners.put(channel, listeners); + } + listeners.add(listener); + channels.add(channel); + } + + + @Override + public void removeListener(JbootmqMessageListener listener) { + globalListeners.remove(listener); + for (List listeners : channelListeners.values()) { + listeners.remove(listener); + } + } + + @Override + public void removeAllListeners() { + globalListeners.clear(); + channelListeners.forEach((s, list) -> list.clear()); + channelListeners.clear(); + } + + + @Override + public Collection getGlobalListeners() { + return globalListeners; + } + + + @Override + public Collection getListenersByChannel(String channel) { + return channelListeners.get(channel); + } + + public void notifyListeners(String channel, Object message, MessageContext context) { + + boolean globalResult = notifyListeners(channel, message, context, globalListeners); + boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); + + if (!globalResult && !channelResult) { + LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + + channel + " message:" + message); + } + } + + + protected boolean notifyListeners(String channel, Object message, MessageContext context, Collection listeners) { + if (listeners == null || listeners.size() == 0) { + return false; + } + + if (syncRecevieMessageChannels.contains(channel)) { + for (JbootmqMessageListener listener : listeners) { + try { + listener.onMessage(channel, message, context); + } catch (Throwable ex) { + LOG.warn("listener[" + listener.getClass().getName() + "] execute mq message is error. channel:" + + channel + " message:" + message); + } + } + } else { + for (JbootmqMessageListener listener : listeners) { + threadPool.execute(() -> { + listener.onMessage(channel, message, context); + }); + } + } + + return true; + } + + + public JbootSerializer getSerializer() { + if (serializer == null) { + serializer = StrUtil.isNotBlank(config.getSerializer()) + ? Jboot.getSerializer(config.getSerializer()) + : Jboot.getSerializer(); + } + return serializer; + } + + + protected boolean isStarted = false; + + @Override + public boolean startListening() { + if (isStarted) { + return true; + } + + if (channels == null || channels.isEmpty()) { + LogKit.warn("Jboot MQ started fail. because it's channels is empty, please config channels. " + + "MQ name: {}, type:{}", config.getName(), config.getType()); + return false; + } + + try { + isStarted = true; + onStartListening(); + } catch (Exception ex) { + LogKit.error("Jboot MQ start fail!", ex); + isStarted = false; + return false; + } + + return true; + } + + + @Override + public boolean stopListening() { + if (!isStarted) { + return true; + } + + try { + isStarted = false; + onStopListening(); + } catch (Exception ex) { + LogKit.error("Jboot MQ stop fail!", ex); + isStarted = true; + return false; + } + + return true; + } + + public boolean isStarted() { + return isStarted; + } + + protected abstract void onStartListening(); + + protected abstract void onStopListening(); + + + @Override + public JbootmqConfig getConfig() { + return config; + } + + public void setSerializer(JbootSerializer serializer) { + this.serializer = serializer; + } + + public ExecutorService getThreadPool() { + return threadPool; + } + + public void setThreadPool(ExecutorService threadPool) { + this.threadPool = threadPool; + } +} diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 011b638c32d8e9f14a78a3d0d852c837c2e48f3d..42a84618bd50ab6c39e7d34e425a4fdb04014b1c 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -1,119 +1,131 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.mq; - -import io.jboot.Jboot; -import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; -import io.jboot.components.mq.local.JbootLocalmqImpl; -import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; -import io.jboot.components.mq.rabbitmq.JbootRabbitmqImpl; -import io.jboot.components.mq.redismq.JbootRedismqImpl; -import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; -import io.jboot.core.spi.JbootSpiLoader; -import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.utils.ClassUtil; -import io.jboot.utils.ConfigUtil; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - - -public class JbootmqManager { - - private static JbootmqManager manager; - - public static JbootmqManager me() { - if (manager == null) { - manager = ClassUtil.singleton(JbootmqManager.class); - } - return manager; - } - - private Map jbootmqMap = new ConcurrentHashMap<>(); - - public Jbootmq getJbootmq() { - return getJbootmq("default"); - } - - - public Jbootmq getJbootmq(String name) { - Jbootmq mq = jbootmqMap.get(name); - if (mq == null) { - synchronized (this) { - mq = jbootmqMap.get(name); - if (mq == null) { - Map configModels = ConfigUtil.getConfigModels(JbootmqConfig.class); - JbootmqConfig.TYPES.forEach(configModels::remove); - - configModels.putIfAbsent("default", Jboot.config(JbootmqConfig.class)); - - if (!configModels.containsKey(name)) { - throw new JbootIllegalConfigException("Please config \"jboot.mq." + name + ".type\" in your jboot.properties."); - } - - mq = getJbootmq(configModels.get(name)); - if (mq != null) { - jbootmqMap.put(name, mq); - } - } - } - } - return mq; - } - - public Jbootmq getJbootmq(JbootmqConfig config) { - return buildJbootmq(config); - } - - private Jbootmq buildJbootmq(JbootmqConfig config) { - if (config == null) { - throw new IllegalArgumentException("config must not be null"); - } - - if (!config.isConfigOk()) { - return null; - } - - switch (config.getType()) { - case JbootmqConfig.TYPE_REDIS: - return new JbootRedismqImpl(config); - case JbootmqConfig.TYPE_ALIYUNMQ: - return new JbootAliyunmqImpl(config); - case JbootmqConfig.TYPE_RABBITMQ: - return new JbootRabbitmqImpl(config); - case JbootmqConfig.TYPE_ROCKETMQ: - return new JbootRocketmqImpl(config); - case JbootmqConfig.TYPE_QPID: - return new JbootQpidmqImpl(config); - case JbootmqConfig.TYPE_ACTIVEMQ: - throw new RuntimeException("not finished!!!!"); - case JbootmqConfig.TYPE_LOCAL: - return new JbootLocalmqImpl(config); - default: - return JbootSpiLoader.load(Jbootmq.class, config.getType(), config); - } - - } - - - public void init() { - jbootmqMap.values().forEach(Jbootmq::startListening); - } - - public void stop() { - jbootmqMap.values().forEach(Jbootmq::stopListening); - } -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.mq; + +import io.jboot.Jboot; +import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; +import io.jboot.components.mq.local.JbootLocalmqImpl; +import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; +import io.jboot.components.mq.rabbitmq.JbootRabbitmqImpl; +import io.jboot.components.mq.redismq.JbootRedismqImpl; +import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; +import io.jboot.core.spi.JbootSpiLoader; +import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.utils.ClassUtil; +import io.jboot.utils.ConfigUtil; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class JbootmqManager { + + private static JbootmqManager manager; + + public static JbootmqManager me() { + if (manager == null) { + manager = ClassUtil.singleton(JbootmqManager.class); + } + return manager; + } + + private Map jbootmqMap = new ConcurrentHashMap<>(); + + public Jbootmq getJbootmq() { + return getJbootmq("default"); + } + + + public Jbootmq getJbootmq(String name) { + Jbootmq mq = jbootmqMap.get(name); + if (mq == null) { + synchronized (this) { + mq = jbootmqMap.get(name); + if (mq == null) { + Map configModels = ConfigUtil.getConfigModels(JbootmqConfig.class); + JbootmqConfig.TYPES.forEach(configModels::remove); + + configModels.putIfAbsent("default", Jboot.config(JbootmqConfig.class)); + + JbootmqConfig mqConfig = null; + if (!configModels.containsKey(name)) { + for (JbootmqConfig config : configModels.values()) { + if (name.equals(config.getTypeName())) { + mqConfig = config; + break; + } + } + if (mqConfig == null) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.other" + name + ".type\" in your jboot.properties."); + } + } + else { + mqConfig = configModels.get(name); + } + + mq = getJbootmq(mqConfig); + if (mq != null) { + jbootmqMap.put(name, mq); + } + } + } + } + return mq; + } + + public Jbootmq getJbootmq(JbootmqConfig config) { + return buildJbootmq(config); + } + + private Jbootmq buildJbootmq(JbootmqConfig config) { + if (config == null) { + throw new IllegalArgumentException("config must not be null"); + } + + if (!config.isConfigOk()) { + return null; + } + + switch (config.getType()) { + case JbootmqConfig.TYPE_REDIS: + return new JbootRedismqImpl(config); + case JbootmqConfig.TYPE_ALIYUNMQ: + return new JbootAliyunmqImpl(config); + case JbootmqConfig.TYPE_RABBITMQ: + return new JbootRabbitmqImpl(config); + case JbootmqConfig.TYPE_ROCKETMQ: + return new JbootRocketmqImpl(config); + case JbootmqConfig.TYPE_QPID: + return new JbootQpidmqImpl(config); + case JbootmqConfig.TYPE_ACTIVEMQ: + throw new RuntimeException("not finished!!!!"); + case JbootmqConfig.TYPE_LOCAL: + return new JbootLocalmqImpl(config); + default: + return JbootSpiLoader.load(Jbootmq.class, config.getType(), config); + } + + } + + + public void init() { + jbootmqMap.values().forEach(Jbootmq::startListening); + } + + public void stop() { + jbootmqMap.values().forEach(Jbootmq::stopListening); + } +} diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index 6153888d494165ae6c3babfe01bf4cad23a8a002..f1249cd368cef983df8405c5303f030edd8ffeee 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -1,136 +1,151 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.mq.redismq; - -import com.jfinal.log.Log; -import io.jboot.Jboot; -import io.jboot.components.mq.Jbootmq; -import io.jboot.components.mq.JbootmqBase; -import io.jboot.components.mq.JbootmqConfig; -import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.support.redis.JbootRedis; -import io.jboot.support.redis.JbootRedisManager; -import io.jboot.utils.ConfigUtil; -import io.jboot.utils.StrUtil; -import redis.clients.jedis.BinaryJedisPubSub; - -import java.util.Map; - - -public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { - - private static final Log LOG = Log.getLog(JbootRedismqImpl.class); - - private JbootRedis redis; - private Thread dequeueThread; - private BinaryJedisPubSub jedisPubSub; - private long interval = 100L; - - public JbootRedismqImpl(JbootmqConfig config) { - super(config); - - JbootRedismqConfig redisConfig = null; - String typeName = config.getTypeName(); - if (StrUtil.isNotBlank(typeName)) { - Map configModels = ConfigUtil.getConfigModels(JbootRedismqConfig.class); - if (!configModels.containsKey(typeName)) { - throw new JbootIllegalConfigException("Please config \"jboot.mq.redis." + typeName + ".host\" in your jboot.properties."); - } - redisConfig = configModels.get(typeName); - } else { - redisConfig = Jboot.config(JbootRedismqConfig.class); - } - - if (redisConfig.isConfigOk()) { - redis = JbootRedisManager.me().getRedis(redisConfig); - } else { - redis = Jboot.getRedis(); - } - - if (redis == null) { - throw new JbootIllegalConfigException("can not use redis mq (redis mq is default), " + - "please config jboot.redis.host=your-host , or use other mq component. "); - } - } - - @Override - protected void onStartListening() { - - String[] channels = this.channels.toArray(new String[]{}); - jedisPubSub = new BinaryJedisPubSub() { - @Override - public void onMessage(byte[] channel, byte[] message) { - notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message) - , new RedismqMessageContext(JbootRedismqImpl.this)); - } - }; - - redis.subscribe(jedisPubSub, redis.keysToBytesArray(channels)); - - dequeueThread = new Thread(this, "redis-dequeue-thread"); - dequeueThread.start(); - } - - @Override - protected void onStopListening() { - if (jedisPubSub != null) { - jedisPubSub.unsubscribe(); - } - dequeueThread.interrupt(); - } - - - @Override - public void enqueue(Object message, String toChannel) { - redis.lpush(toChannel, message); - } - - - @Override - public void publish(Object message, String toChannel) { - redis.publish(redis.keyToBytes(toChannel), getSerializer().serialize(message)); - } - - - @Override - public void run() { - while (isStarted) { - try { - doExecuteDequeue(); - Thread.sleep(interval); - } catch (Exception ex) { - LOG.error(ex.toString(), ex); - } - } - } - - public void doExecuteDequeue() { - for (String channel : this.channels) { - Object data = redis.rpop(channel); - if (data != null) { - notifyListeners(channel, data, new RedismqMessageContext(JbootRedismqImpl.this)); - } - } - } - - public long getInterval() { - return interval; - } - - public void setInterval(long interval) { - this.interval = interval; - } -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.mq.redismq; + +import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.components.mq.Jbootmq; +import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; +import io.jboot.components.mq.JbootmqMessageListener; +import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.support.redis.JbootRedis; +import io.jboot.support.redis.JbootRedisManager; +import io.jboot.utils.ConfigUtil; +import io.jboot.utils.StrUtil; +import redis.clients.jedis.BinaryJedisPubSub; + +import java.util.HashMap; +import java.util.Map; + + +public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { + + private static final Log LOG = Log.getLog(JbootRedismqImpl.class); + + private JbootRedis redis; + private Thread dequeueThread; + private BinaryJedisPubSub jedisPubSub; + private long interval = 100L; + + private Integer database = 0; + + public JbootRedismqImpl(JbootmqConfig config) { + super(config); + + JbootRedismqConfig redisConfig = null; + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = ConfigUtil.getConfigModels(JbootRedismqConfig.class); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.redis." + typeName + ".host\" in your jboot.properties."); + } + redisConfig = configModels.get(typeName); + } else { + redisConfig = Jboot.config(JbootRedismqConfig.class); + } + + database = redisConfig.getDatabase(); + + if (redisConfig.isConfigOk()) { + redis = JbootRedisManager.me().getRedis(redisConfig); + } else { + redis = Jboot.getRedis(); + } + + if (redis == null) { + throw new JbootIllegalConfigException("can not use redis mq (redis mq is default), " + + "please config jboot.redis.host=your-host , or use other mq component. "); + } + } + + private Map outterChannelMap = new HashMap<>(); + + @Override + protected void onStartListening() { + String[] channels = this.channels.toArray(new String[]{}); + jedisPubSub = new BinaryJedisPubSub() { + @Override + public void onMessage(byte[] channel, byte[] message) { + String thisChannel = redis.bytesToKey(channel); + String realChannel = outterChannelMap.get(thisChannel); + if (realChannel == null) { + LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + thisChannel); + } + notifyListeners(realChannel, getSerializer().deserialize(message) + , new RedismqMessageContext(JbootRedismqImpl.this)); + } + }; + + for (int i = 0; i< channels.length; i++) { + outterChannelMap.put(channels[i] + "_" + database, channels[i]); + channels[i] = channels[i] + "_" + database; + } + redis.subscribe(jedisPubSub, redis.keysToBytesArray(channels)); + + dequeueThread = new Thread(this, "redis-dequeue-thread"); + dequeueThread.start(); + } + + @Override + protected void onStopListening() { + if (jedisPubSub != null) { + jedisPubSub.unsubscribe(); + } + dequeueThread.interrupt(); + } + + + @Override + public void enqueue(Object message, String toChannel) { + redis.lpush(toChannel + "_" + database, message); + } + + + @Override + public void publish(Object message, String toChannel) { + redis.publish(redis.keyToBytes(toChannel + "_" + database), getSerializer().serialize(message)); + } + + @Override + public void run() { + while (isStarted) { + try { + doExecuteDequeue(); + Thread.sleep(interval); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + } + + public void doExecuteDequeue() { + for (String channel : this.channels) { + Object data = redis.lpop(channel + "_" + database); + if (data != null) { + notifyListeners(channel, data, new RedismqMessageContext(JbootRedismqImpl.this)); + } + } + } + + public long getInterval() { + return interval; + } + + public void setInterval(long interval) { + this.interval = interval; + } +} diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index d2505f6561c52dfdb89ea3b882da3abdb16e77ab..03cb05dab93350fa1596dcfadb78b7fad80757d4 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -1,824 +1,878 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.utils; - -import io.jboot.app.config.JbootConfigManager; - -import java.io.File; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLDecoder; -import java.util.*; -import java.util.function.Predicate; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.stream.Collectors; - -public class ClassScanner { - - private static final Set appClassesCache = new HashSet<>(); - - public static final Set scanJars = new HashSet<>(); - public static final Set excludeJars = new HashSet<>(); - - public static final Set scanClasses = new HashSet<>(); - public static final Set excludeClasses = new HashSet<>(); - - // 默认关闭扫描信息的控制台输出 - private static boolean printScannerInfoEnable = false; - - public static boolean isPrintScannerInfoEnable() { - return printScannerInfoEnable; - } - - public static void setPrintScannerInfoEnable(boolean printScannerInfoEnable) { - ClassScanner.printScannerInfoEnable = printScannerInfoEnable; - } - - - public static void addScanJarPrefix(String prefix) { - scanJars.add(prefix.toLowerCase().trim()); - } - - static { - scanJars.add("jboot"); - } - - - public static void addUnscanJarPrefix(String prefix) { - excludeJars.add(prefix.toLowerCase().trim()); - } - - static { - excludeJars.add("jfinal-"); - excludeJars.add("cos-"); - excludeJars.add("cglib-"); - excludeJars.add("undertow-"); - excludeJars.add("xnio-"); - excludeJars.add("javax."); - excludeJars.add("hikaricp-"); - excludeJars.add("druid-"); - excludeJars.add("mysql-"); - excludeJars.add("db2jcc-"); - excludeJars.add("db2jcc4-"); - excludeJars.add("ojdbc"); - excludeJars.add("junit-"); - excludeJars.add("junit5-"); - excludeJars.add("org.junit"); - excludeJars.add("hamcrest-"); - excludeJars.add("jboss-"); - excludeJars.add("motan-"); - excludeJars.add("commons-pool"); - excludeJars.add("commons-beanutils"); - excludeJars.add("commons-codec"); - excludeJars.add("commons-collections"); - excludeJars.add("commons-configuration"); - excludeJars.add("commons-lang"); - excludeJars.add("commons-logging"); - excludeJars.add("commons-io"); - excludeJars.add("commons-httpclient"); - excludeJars.add("commons-fileupload"); - excludeJars.add("commons-validator"); - excludeJars.add("commons-email"); - excludeJars.add("commons-text"); - excludeJars.add("commons-cli"); - excludeJars.add("commons-math"); - excludeJars.add("commons-jxpath"); - excludeJars.add("commons-compress"); - excludeJars.add("audience-"); - excludeJars.add("hessian-"); - excludeJars.add("metrics-"); - excludeJars.add("javapoet-"); - excludeJars.add("netty-"); - excludeJars.add("consul-"); - excludeJars.add("gson-"); - excludeJars.add("zookeeper-"); - excludeJars.add("slf4j-"); - excludeJars.add("fastjson-"); - excludeJars.add("guava-"); - excludeJars.add("failureaccess-"); - excludeJars.add("listenablefuture-"); - excludeJars.add("jsr305-"); - excludeJars.add("checker-qual-"); - excludeJars.add("error_prone_annotations-"); - excludeJars.add("j2objc-"); - excludeJars.add("animal-sniffer-"); - excludeJars.add("cron4j-"); - excludeJars.add("jedis-"); - excludeJars.add("lettuce-"); - excludeJars.add("reactor-"); - excludeJars.add("fst-"); - excludeJars.add("kryo-"); - excludeJars.add("jackson-"); - excludeJars.add("javassist-"); - excludeJars.add("objenesis-"); - excludeJars.add("reflectasm-"); - excludeJars.add("asm-"); - excludeJars.add("minlog-"); - excludeJars.add("jsoup-"); - excludeJars.add("ons-client-"); - excludeJars.add("amqp-client-"); - excludeJars.add("ehcache-"); - excludeJars.add("sharding-"); - excludeJars.add("snakeyaml-"); - excludeJars.add("groovy-"); - excludeJars.add("profiler-"); - excludeJars.add("joda-time-"); - excludeJars.add("shiro-"); - excludeJars.add("dubbo-"); - excludeJars.add("curator-"); - excludeJars.add("resteasy-"); - excludeJars.add("reactive-"); - excludeJars.add("validation-"); - excludeJars.add("httpclient-"); - excludeJars.add("httpcore-"); - excludeJars.add("httpmime-"); - excludeJars.add("jcip-"); - excludeJars.add("jcl-"); - excludeJars.add("microprofile-"); - excludeJars.add("org.osgi"); - excludeJars.add("zkclient-"); - excludeJars.add("jjwt-"); - excludeJars.add("okhttp-"); - excludeJars.add("okio-"); - excludeJars.add("zbus-"); - excludeJars.add("swagger-"); - excludeJars.add("j2cache-"); - excludeJars.add("caffeine-"); - excludeJars.add("jline-"); - excludeJars.add("qpid-"); - excludeJars.add("geronimo-"); - excludeJars.add("activation-"); - excludeJars.add("org.abego"); - excludeJars.add("antlr-"); - excludeJars.add("antlr4-"); - excludeJars.add("st4-"); - excludeJars.add("icu4j-"); - excludeJars.add("idea_rt"); - excludeJars.add("mrjtoolkit"); - excludeJars.add("logback-"); - excludeJars.add("log4j-"); - excludeJars.add("log4j2-"); - excludeJars.add("aliyun-java-sdk-"); - excludeJars.add("aliyun-sdk-"); - excludeJars.add("archaius-"); - excludeJars.add("aopalliance-"); - excludeJars.add("hdrhistogram-"); - excludeJars.add("jdom-"); - excludeJars.add("rxjava-"); - excludeJars.add("jersey-"); - excludeJars.add("stax-"); - excludeJars.add("stax2-"); - excludeJars.add("jettison-"); - excludeJars.add("commonmark-"); - excludeJars.add("jaxb-"); - excludeJars.add("json-20"); - excludeJars.add("jcseg-"); - excludeJars.add("lucene-"); - excludeJars.add("elasticsearch-"); - excludeJars.add("jopt-"); - excludeJars.add("httpasyncclient-"); - excludeJars.add("jna-"); - excludeJars.add("lang-mustache-client-"); - excludeJars.add("parent-join-client-"); - excludeJars.add("rank-eval-client-"); - excludeJars.add("aggs-matrix-stats-client-"); - excludeJars.add("t-digest-"); - excludeJars.add("compiler-"); - excludeJars.add("hppc-"); - excludeJars.add("libthrift-"); - excludeJars.add("seata-"); - excludeJars.add("eureka-"); - excludeJars.add("netflix-"); - excludeJars.add("nacos-"); - excludeJars.add("apollo-"); - excludeJars.add("guice-"); - excludeJars.add("servlet-"); - excludeJars.add("debugger-agent.jar"); - excludeJars.add("xpp3_min-"); - excludeJars.add("latency"); - excludeJars.add("micrometer-"); - excludeJars.add("xstream-"); - excludeJars.add("jsr311-"); - excludeJars.add("servo-"); - excludeJars.add("compactmap-"); - excludeJars.add("dexx-"); - excludeJars.add("spotbugs-"); - excludeJars.add("xmlpull-"); - excludeJars.add("shardingsphere-"); - excludeJars.add("sentinel-"); - excludeJars.add("spring-"); - excludeJars.add("simpleclient-"); - excludeJars.add("breeze-"); - excludeJars.add("config-"); - excludeJars.add("encrypt-core-"); - excludeJars.add("lombok-"); - excludeJars.add("hutool-"); - excludeJars.add("jakarta."); - excludeJars.add("protostuff-"); - excludeJars.add("poi-"); - excludeJars.add("easypoi-"); - excludeJars.add("ognl-"); - excludeJars.add("xmlbeans-"); - excludeJars.add("master-slave-core-"); - excludeJars.add("shadow-core-rewrite-"); - excludeJars.add("apiguardian-api-"); - excludeJars.add("opentest4j-"); - excludeJars.add("opentracing-"); - excludeJars.add("freemarker-"); - excludeJars.add("protobuf-"); - excludeJars.add("jdom2-"); - excludeJars.add("useragentutils-"); - excludeJars.add("common-io-"); - excludeJars.add("common-image-"); - excludeJars.add("common-lang-"); - excludeJars.add("imageio-"); - excludeJars.add("curvesapi-"); - excludeJars.add("myexcel-"); - excludeJars.add("oshi-"); - excludeJars.add("classmate-"); - excludeJars.add("hibernate-"); - excludeJars.add("aspectjweaver-"); - excludeJars.add("aspectjrt-"); - excludeJars.add("simpleclient_"); - excludeJars.add("rocketmq-"); - excludeJars.add("clickhouse-"); - excludeJars.add("lz4-"); - excludeJars.add("commons-digester-"); - excludeJars.add("opencc4j-"); - excludeJars.add("heaven-"); - excludeJars.add("tinypinyin-"); - excludeJars.add("jieba-"); - excludeJars.add("ahocorasick-"); - excludeJars.add("kotlin-"); - excludeJars.add("xml-apis-"); - excludeJars.add("dom4j-"); - excludeJars.add("ini4j-"); - excludeJars.add("cache-api-"); - excludeJars.add("byte-buddy-"); - excludeJars.add("jodd-"); - excludeJars.add("redisson-"); - excludeJars.add("bcprov-"); - excludeJars.add("pay-java-"); - excludeJars.add("alipay-sdk-"); - excludeJars.add("mapper-extras-"); - excludeJars.add("org.jacoco"); - excludeJars.add("jxl-"); - excludeJars.add("jxls-"); - excludeJars.add("jstl-"); - excludeJars.add("batik-"); - excludeJars.add("xmlsec-"); - excludeJars.add("pdfbox-"); - excludeJars.add("fontbox-"); - excludeJars.add("xk-time-"); - excludeJars.add("geohash-"); - excludeJars.add("ezmorph-"); - excludeJars.add("async-http-"); - excludeJars.add("jsr-"); - excludeJars.add("jsr250"); - excludeJars.add("pinyin4j"); - excludeJars.add("ijpay-"); - excludeJars.add("wildfly-"); - excludeJars.add("liquibase-"); - excludeJars.add("flowable-"); - excludeJars.add("mybatis-"); - excludeJars.add("ip2region-"); - excludeJars.add("java-uuid-generator-"); - excludeJars.add("quartz-"); - } - - - public static void addUnscanClassPrefix(String prefix) { - excludeClasses.add(prefix.trim()); - } - - static { - excludeClasses.add("java."); - excludeClasses.add("javax."); - excludeClasses.add("junit."); - excludeClasses.add("jline."); - excludeClasses.add("redis."); - excludeClasses.add("lombok."); - excludeClasses.add("oshi."); - excludeClasses.add("jodd."); - excludeClasses.add("javassist."); - excludeClasses.add("google."); - excludeClasses.add("com.jfinal."); - excludeClasses.add("com.aliyuncs."); - excludeClasses.add("com.carrotsearch."); - excludeClasses.add("org.aopalliance."); - excludeClasses.add("org.apache."); - excludeClasses.add("org.nustaq."); - excludeClasses.add("net.sf."); - excludeClasses.add("org.slf4j."); - excludeClasses.add("org.antlr."); - excludeClasses.add("org.jboss."); - excludeClasses.add("org.checkerframework."); - excludeClasses.add("org.jsoup."); - excludeClasses.add("org.objenesis."); - excludeClasses.add("org.ow2."); - excludeClasses.add("org.reactivest."); - excludeClasses.add("org.yaml."); - excludeClasses.add("org.checker"); - excludeClasses.add("org.codehaus"); - excludeClasses.add("org.commonmark"); - excludeClasses.add("org.jdom2."); - excludeClasses.add("org.aspectj."); - excludeClasses.add("org.hibernate."); - excludeClasses.add("org.ahocorasick."); - excludeClasses.add("org.lionsoul.jcseg."); - excludeClasses.add("org.ini4j."); - excludeClasses.add("org.jetbrains."); - excludeClasses.add("org.jacoco."); - excludeClasses.add("org.xnio."); - excludeClasses.add("org.bouncycastle."); - excludeClasses.add("org.elasticsearch."); - excludeClasses.add("org.hamcrest."); - excludeClasses.add("org.objectweb."); - excludeClasses.add("org.joda."); - excludeClasses.add("org.wildfly."); - excludeClasses.add("org.owasp."); - excludeClasses.add("aj.org."); - excludeClasses.add("ch.qos."); - excludeClasses.add("joptsimple."); - excludeClasses.add("com.alibaba.csp."); - excludeClasses.add("com.alibaba.nacos."); - excludeClasses.add("com.alibaba.druid."); - excludeClasses.add("com.alibaba.fastjson."); - excludeClasses.add("com.aliyun.open"); - excludeClasses.add("com.caucho"); - excludeClasses.add("com.codahale"); - excludeClasses.add("com.ctrip.framework.apollo"); - excludeClasses.add("com.ecwid."); - excludeClasses.add("com.esotericsoftware."); - excludeClasses.add("com.fasterxml."); - excludeClasses.add("com.github."); - excludeClasses.add("io.github."); - excludeClasses.add("com.google."); - excludeClasses.add("metrics_influxdb."); - excludeClasses.add("com.rabbitmq."); - excludeClasses.add("com.squareup."); - excludeClasses.add("com.sun."); - excludeClasses.add("com.typesafe."); - excludeClasses.add("com.weibo.api.motan."); - excludeClasses.add("com.zaxxer."); - excludeClasses.add("com.mysql."); - excludeClasses.add("com.papertrail."); - excludeClasses.add("com.egzosn."); - excludeClasses.add("com.alipay.api"); - excludeClasses.add("org.gjt."); - excludeClasses.add("org.fusesource."); - excludeClasses.add("org.redisson."); - excludeClasses.add("io.dropwizard"); - excludeClasses.add("io.prometheus"); - excludeClasses.add("io.jsonwebtoken"); - excludeClasses.add("io.lettuce"); - excludeClasses.add("reactor.adapter"); - excludeClasses.add("io.seata."); - excludeClasses.add("io.swagger."); - excludeClasses.add("io.undertow."); - excludeClasses.add("io.netty."); - excludeClasses.add("io.opentracing."); - excludeClasses.add("it.sauronsoftware"); - excludeClasses.add("net.oschina.j2cache"); - excludeClasses.add("net.bytebuddy"); - excludeClasses.add("cn.hutool."); - excludeClasses.add("com.dyuproject."); - excludeClasses.add("io.protostuff."); - excludeClasses.add("io.reactivex."); - excludeClasses.add("freemarker."); - excludeClasses.add("com.twelvemonkeys."); - excludeClasses.add("eu.bitwalker."); - excludeClasses.add("jstl."); - excludeClasses.add("jxl."); - excludeClasses.add("org.jxls"); - excludeClasses.add("org.kordamp"); - excludeClasses.add("org.mybatis"); - excludeClasses.add("org.lisonsoul"); - excludeClasses.add("org.flowable"); - } - - - public static void addScanClassPrefix(String prefix) { - scanClasses.add(prefix.toLowerCase().trim()); - } - - static { - scanClasses.add("io.jboot.support.shiro.directives"); - } - - static { - String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); - if (scanJarPrefix != null) { - String[] prefixes = scanJarPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addScanJarPrefix(prefix.trim()); - } - } - } - - String unScanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanJarPrefix"); - if (unScanJarPrefix != null) { - String[] prefixes = unScanJarPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addUnscanJarPrefix(prefix.trim()); - } - } - } - - String unScanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanClassPrefix"); - if (unScanClassPrefix != null) { - String[] prefixes = unScanClassPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addUnscanClassPrefix(prefix.trim()); - } - } - } - - String scanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanClassPrefix"); - if (scanClassPrefix != null) { - String[] prefixes = scanClassPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addScanClassPrefix(prefix.trim()); - } - } - } - - } - - public static List> scanSubClass(Class pclazz) { - return scanSubClass(pclazz, false); - } - - - public static List> scanSubClass(Class pclazz, boolean instantiable) { - initIfNecessary(); - List> classes = new ArrayList<>(); - findChildClasses(classes, pclazz, instantiable); - return classes; - } - - public static List scanClass() { - return scanClass(false); - } - - public static List scanClass(boolean isInstantiable) { - - initIfNecessary(); - - if (!isInstantiable) { - return new ArrayList<>(appClassesCache); - } - - return scanClass(ClassScanner::isInstantiable); - - } - - public static List scanClass(Predicate filter) { - - initIfNecessary(); - - return appClassesCache.stream() - .filter(filter) - .collect(Collectors.toList()); - - } - - public static void clearAppClassesCache() { - appClassesCache.clear(); - } - - - private static boolean isInstantiable(Class clazz) { - return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); - } - - - public static List scanClassByAnnotation(Class annotationClass, boolean instantiable) { - initIfNecessary(); - - List list = new ArrayList<>(); - for (Class clazz : appClassesCache) { - Annotation annotation = clazz.getAnnotation(annotationClass); - if (annotation == null) { - continue; - } - - if (instantiable && !isInstantiable(clazz)) { - continue; - } - - list.add(clazz); - } - - return list; - } - - private static void initIfNecessary() { - if (appClassesCache.isEmpty()) { - initAppClasses(); - } - } - - - private static void findChildClasses(List> classes, Class parent, boolean instantiable) { - for (Class clazz : appClassesCache) { - - if (!parent.isAssignableFrom(clazz)) { - continue; - } - - if (instantiable && !isInstantiable(clazz)) { - continue; - } - - classes.add(clazz); - } - } - - - private static void initAppClasses() { - - Set jarPaths = new HashSet<>(); - Set classPaths = new HashSet<>(); - - - // jdk8 及以下、 - // tomcat 容器、 - // jfinal-undertow、 - // 以上三种加载模式通过 classloader 获取 - findClassPathsAndJarsByClassloader(jarPaths, classPaths, ClassScanner.class.getClassLoader()); - - //jdk9+ 等其他方式通过 classpath 获取 - findClassPathsAndJarsByClassPath(jarPaths, classPaths); - - - String tomcatClassPath = null; - - for (String classPath : classPaths) { - //过滤tomcat自身的lib 以及 bin 下的jar - File tomcatApiJarFile = new File(classPath, "tomcat-api.jar"); - File tomcatJuliJarFile = new File(classPath, "tomcat-juli.jar"); - if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { - tomcatClassPath = tomcatApiJarFile - .getParentFile() - .getParentFile().getAbsolutePath().replace('\\', '/'); - continue; - } - - if (isPrintScannerInfoEnable()) { - System.out.println("Jboot Scan ClassPath: " + classPath); - } - - addClassesFromClassPath(classPath); - } - - for (String jarPath : jarPaths) { - - //过滤 tomcat 的 jar,但是不能过滤 webapps 目录下的 - if (tomcatClassPath != null - && jarPath.startsWith(tomcatClassPath) - && !jarPath.contains("webapps")) { - continue; - } - - if (!isIncludeJar(jarPath)) { - continue; - } - - if (isPrintScannerInfoEnable()) { - System.out.println("Jboot Scan Jar: " + jarPath); - } - - addClassesFromJar(jarPath); - } - - - } - - private static void addClassesFromJar(String jarPath) { - JarFile jarFile = null; - try { - jarFile = new JarFile(jarPath); - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry jarEntry = entries.nextElement(); - if (!jarEntry.isDirectory()) { - String entryName = jarEntry.getName(); - if (entryName.endsWith(".class")) { - String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); - addClass(classForName(className)); - } - } - } - } catch (IOException e1) { - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException e) { - } - } - } - } - - - private static void addClassesFromClassPath(String classPath) { - - List classFileList = new ArrayList<>(); - scanClassFile(classFileList, classPath); - - for (File file : classFileList) { - - int start = classPath.length(); - int end = file.toString().length() - ".class".length(); - - String classFile = file.toString().substring(start + 1, end); - String className = classFile.replace(File.separator, "."); - - addClass(classForName(className)); - } - } - - private static void addClass(Class clazz) { - if (clazz != null && isNotExcludeClass(clazz.getName())) { - appClassesCache.add(clazz); - } - } - - //用于在进行 fatjar 打包时,提高性能 - private static boolean isNotExcludeClass(String clazzName) { - for (String prefix : scanClasses) { - if (clazzName.startsWith(prefix)) { - return true; - } - } - for (String prefix : excludeClasses) { - if (clazzName.startsWith(prefix)) { - return false; - } - } - return true; - } - - - private static void findClassPathsAndJarsByClassloader(Set jarPaths, Set classPaths, ClassLoader classLoader) { - try { - URL[] urls = null; - if (classLoader instanceof URLClassLoader) { - URLClassLoader ucl = (URLClassLoader) classLoader; - urls = ucl.getURLs(); - } - if (urls != null) { - for (URL url : urls) { - String path = url.getPath(); - path = URLDecoder.decode(path, "UTF-8"); - - // path : /d:/xxx - if (path.startsWith("/") && path.indexOf(":") == 2) { - path = path.substring(1); - } - - if (!path.toLowerCase().endsWith(".jar")) { - classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } else { - jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } - } - } - } catch (Exception ex) { - ex.printStackTrace(); - } - - ClassLoader parent = classLoader.getParent(); - if (parent != null) { - findClassPathsAndJarsByClassloader(jarPaths, classPaths, parent); - } - } - - private static void findClassPathsAndJarsByClassPath(Set jarPaths, Set classPaths) { - String classPath = System.getProperty("java.class.path"); - if (classPath == null || classPath.trim().length() == 0) { - return; - } - String[] classPathArray = classPath.split(File.pathSeparator); - if (classPathArray == null || classPathArray.length == 0) { - return; - } - for (String path : classPathArray) { - path = path.trim(); - - if (path.startsWith("./")) { - path = path.substring(2); - } - - if (path.startsWith("/") && path.indexOf(":") == 2) { - path = path.substring(1); - } - try { - if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { - classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } else { - jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } - } catch (IOException e) { - } - } - } - - - private static boolean isIncludeJar(String path) { - - String jarName = new File(path).getName().toLowerCase(); - - for (String include : scanJars) { - if (jarName.startsWith(include)) { - return true; - } - } - - for (String exclude : excludeJars) { - if (jarName.startsWith(exclude)) { - return false; - } - } - - //from jre lib - if (path.contains("/jre/lib")) { - return false; - } - - //from java home - if (getJavaHome() != null - && path.startsWith(getJavaHome())) { - return false; - } - - return true; - } - - - @SuppressWarnings("unchecked") - private static Class classForName(String className) { - try { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return Class.forName(className, false, cl); - } catch (Throwable ex) { - //ignore - } - return null; - } - - - private static void scanClassFile(List fileList, String path) { - File[] files = new File(path).listFiles(); - if (null == files || files.length == 0) { - return; - } - for (File file : files) { - if (file.isDirectory()) { - scanClassFile(fileList, file.getAbsolutePath()); - } else if (file.getName().endsWith(".class")) { - fileList.add(file); - } - } - } - - - private static String javaHome; - - private static String getJavaHome() { - if (javaHome == null) { - try { - String javaHomeString = System.getProperty("java.home"); - if (javaHomeString != null && javaHomeString.trim().length() > 0) { - javaHome = new File(javaHomeString, "..").getCanonicalPath().replace('\\', '/'); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - return javaHome; - } - -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.stream.Collectors; + +import io.jboot.app.config.JbootConfigManager; + +public class ClassScanner { + + private static final Set> appClassesCache = new HashSet<>(); + + public static final Set scanJars = new HashSet<>(); + public static final Set excludeJars = new HashSet<>(); + + public static final Set scanClasses = new HashSet<>(); + public static final Set excludeClasses = new HashSet<>(); + // dev模式打开扫描信息打印 + private static boolean printScannerInfoEnable = JbootConfigManager.me().isDevMode(); + + public static boolean isPrintScannerInfoEnable() { + return printScannerInfoEnable; + } + + public static void setPrintScannerInfoEnable(boolean printScannerInfoEnable) { + ClassScanner.printScannerInfoEnable = printScannerInfoEnable; + } + + + public static void addScanJarPrefix(String prefix) { + scanJars.add(prefix.toLowerCase().trim()); + } + + static { + scanJars.add("jboot"); + } + + + public static void addUnscanJarPrefix(String prefix) { + excludeJars.add(prefix.toLowerCase().trim()); + } + + static { + excludeJars.add("jfinal-"); + excludeJars.add("cos-"); + excludeJars.add("cglib-"); + excludeJars.add("undertow-"); + excludeJars.add("xnio-"); + excludeJars.add("javax."); + excludeJars.add("hikaricp-"); + excludeJars.add("druid-"); + excludeJars.add("mysql-"); + excludeJars.add("db2jcc-"); + excludeJars.add("db2jcc4-"); + excludeJars.add("ojdbc"); + excludeJars.add("junit-"); + excludeJars.add("junit5-"); + excludeJars.add("org.junit"); + excludeJars.add("hamcrest-"); + excludeJars.add("jboss-"); + excludeJars.add("motan-"); + excludeJars.add("commons-pool"); + excludeJars.add("commons-beanutils"); + excludeJars.add("commons-codec"); + excludeJars.add("commons-collections"); + excludeJars.add("commons-configuration"); + excludeJars.add("commons-lang"); + excludeJars.add("commons-logging"); + excludeJars.add("commons-io"); + excludeJars.add("commons-httpclient"); + excludeJars.add("commons-fileupload"); + excludeJars.add("commons-validator"); + excludeJars.add("commons-email"); + excludeJars.add("commons-text"); + excludeJars.add("commons-cli"); + excludeJars.add("commons-math"); + excludeJars.add("commons-jxpath"); + excludeJars.add("commons-compress"); + excludeJars.add("audience-"); + excludeJars.add("hessian-"); + excludeJars.add("metrics-"); + excludeJars.add("javapoet-"); + excludeJars.add("netty-"); + excludeJars.add("consul-"); + excludeJars.add("gson-"); + excludeJars.add("zookeeper-"); + excludeJars.add("slf4j-"); + excludeJars.add("fastjson-"); + excludeJars.add("guava-"); + excludeJars.add("failureaccess-"); + excludeJars.add("listenablefuture-"); + excludeJars.add("jsr305-"); + excludeJars.add("checker-qual-"); + excludeJars.add("error_prone_annotations-"); + excludeJars.add("j2objc-"); + excludeJars.add("animal-sniffer-"); + excludeJars.add("cron4j-"); + excludeJars.add("jedis-"); + excludeJars.add("lettuce-"); + excludeJars.add("reactor-"); + excludeJars.add("fst-"); + excludeJars.add("kryo-"); + excludeJars.add("jackson-"); + excludeJars.add("javassist-"); + excludeJars.add("objenesis-"); + excludeJars.add("reflectasm-"); + excludeJars.add("asm-"); + excludeJars.add("minlog-"); + excludeJars.add("jsoup-"); + excludeJars.add("ons-client-"); + excludeJars.add("amqp-client-"); + excludeJars.add("ehcache-"); + excludeJars.add("sharding-"); + excludeJars.add("snakeyaml-"); + excludeJars.add("groovy-"); + excludeJars.add("profiler-"); + excludeJars.add("joda-time-"); + excludeJars.add("shiro-"); + excludeJars.add("dubbo-"); + excludeJars.add("curator-"); + excludeJars.add("resteasy-"); + excludeJars.add("reactive-"); + excludeJars.add("validation-"); + excludeJars.add("httpclient-"); + excludeJars.add("httpcore-"); + excludeJars.add("httpmime-"); + excludeJars.add("jcip-"); + excludeJars.add("jcl-"); + excludeJars.add("microprofile-"); + excludeJars.add("org.osgi"); + excludeJars.add("zkclient-"); + excludeJars.add("jjwt-"); + excludeJars.add("okhttp-"); + excludeJars.add("okio-"); + excludeJars.add("zbus-"); + excludeJars.add("swagger-"); + excludeJars.add("j2cache-"); + excludeJars.add("caffeine-"); + excludeJars.add("jline-"); + excludeJars.add("qpid-"); + excludeJars.add("geronimo-"); + excludeJars.add("activation-"); + excludeJars.add("org.abego"); + excludeJars.add("antlr-"); + excludeJars.add("antlr4-"); + excludeJars.add("st4-"); + excludeJars.add("icu4j-"); + excludeJars.add("idea_rt"); + excludeJars.add("mrjtoolkit"); + excludeJars.add("logback-"); + excludeJars.add("log4j-"); + excludeJars.add("log4j2-"); + excludeJars.add("aliyun-java-sdk-"); + excludeJars.add("aliyun-sdk-"); + excludeJars.add("archaius-"); + excludeJars.add("aopalliance-"); + excludeJars.add("hdrhistogram-"); + excludeJars.add("jdom-"); + excludeJars.add("rxjava-"); + excludeJars.add("jersey-"); + excludeJars.add("stax-"); + excludeJars.add("stax2-"); + excludeJars.add("jettison-"); + excludeJars.add("commonmark-"); + excludeJars.add("jaxb-"); + excludeJars.add("json-20"); + excludeJars.add("jcseg-"); + excludeJars.add("lucene-"); + excludeJars.add("elasticsearch-"); + excludeJars.add("jopt-"); + excludeJars.add("httpasyncclient-"); + excludeJars.add("jna-"); + excludeJars.add("lang-mustache-client-"); + excludeJars.add("parent-join-client-"); + excludeJars.add("rank-eval-client-"); + excludeJars.add("aggs-matrix-stats-client-"); + excludeJars.add("t-digest-"); + excludeJars.add("compiler-"); + excludeJars.add("hppc-"); + excludeJars.add("libthrift-"); + excludeJars.add("seata-"); + excludeJars.add("eureka-"); + excludeJars.add("netflix-"); + excludeJars.add("nacos-"); + excludeJars.add("apollo-"); + excludeJars.add("guice-"); + excludeJars.add("servlet-"); + excludeJars.add("debugger-agent.jar"); + excludeJars.add("xpp3_min-"); + excludeJars.add("latency"); + excludeJars.add("micrometer-"); + excludeJars.add("xstream-"); + excludeJars.add("jsr311-"); + excludeJars.add("servo-"); + excludeJars.add("compactmap-"); + excludeJars.add("dexx-"); + excludeJars.add("spotbugs-"); + excludeJars.add("xmlpull-"); + excludeJars.add("shardingsphere-"); + excludeJars.add("sentinel-"); + excludeJars.add("spring-"); + excludeJars.add("simpleclient-"); + excludeJars.add("breeze-"); + excludeJars.add("config-"); + excludeJars.add("encrypt-core-"); + excludeJars.add("lombok-"); + excludeJars.add("hutool-"); + excludeJars.add("jakarta."); + excludeJars.add("protostuff-"); + excludeJars.add("poi-"); + excludeJars.add("easypoi-"); + excludeJars.add("ognl-"); + excludeJars.add("xmlbeans-"); + excludeJars.add("master-slave-core-"); + excludeJars.add("shadow-core-rewrite-"); + excludeJars.add("apiguardian-api-"); + excludeJars.add("opentest4j-"); + excludeJars.add("opentracing-"); + excludeJars.add("freemarker-"); + excludeJars.add("protobuf-"); + excludeJars.add("jdom2-"); + excludeJars.add("useragentutils-"); + excludeJars.add("common-io-"); + excludeJars.add("common-image-"); + excludeJars.add("common-lang-"); + excludeJars.add("imageio-"); + excludeJars.add("curvesapi-"); + excludeJars.add("myexcel-"); + excludeJars.add("oshi-"); + excludeJars.add("classmate-"); + excludeJars.add("hibernate-"); + excludeJars.add("aspectjweaver-"); + excludeJars.add("aspectjrt-"); + excludeJars.add("simpleclient_"); + excludeJars.add("rocketmq-"); + excludeJars.add("clickhouse-"); + excludeJars.add("lz4-"); + excludeJars.add("commons-digester-"); + excludeJars.add("opencc4j-"); + excludeJars.add("heaven-"); + excludeJars.add("tinypinyin-"); + excludeJars.add("jieba-"); + excludeJars.add("ahocorasick-"); + excludeJars.add("kotlin-"); + excludeJars.add("xml-apis-"); + excludeJars.add("dom4j-"); + excludeJars.add("ini4j-"); + excludeJars.add("cache-api-"); + excludeJars.add("byte-buddy-"); + excludeJars.add("jodd-"); + excludeJars.add("redisson-"); + excludeJars.add("bcprov-"); + excludeJars.add("pay-java-"); + excludeJars.add("alipay-sdk-"); + excludeJars.add("mapper-extras-"); + excludeJars.add("org.jacoco"); + excludeJars.add("jxl-"); + excludeJars.add("jxls-"); + excludeJars.add("jstl-"); + excludeJars.add("batik-"); + excludeJars.add("xmlsec-"); + excludeJars.add("pdfbox-"); + excludeJars.add("fontbox-"); + excludeJars.add("xk-time-"); + excludeJars.add("geohash-"); + excludeJars.add("ezmorph-"); + excludeJars.add("async-http-"); + excludeJars.add("jsr-"); + excludeJars.add("jsr250"); + excludeJars.add("pinyin4j"); + excludeJars.add("ijpay-"); + excludeJars.add("wildfly-"); + excludeJars.add("liquibase-"); + excludeJars.add("flowable-"); + excludeJars.add("mybatis-"); + excludeJars.add("ip2region-"); + excludeJars.add("java-uuid-generator-"); + excludeJars.add("quartz-"); + } + + + public static void addUnscanClassPrefix(String prefix) { + excludeClasses.add(prefix.trim()); + } + + static { + excludeClasses.add("java."); + excludeClasses.add("javax."); + excludeClasses.add("junit."); + excludeClasses.add("jline."); + excludeClasses.add("redis."); + excludeClasses.add("lombok."); + excludeClasses.add("oshi."); + excludeClasses.add("jodd."); + excludeClasses.add("javassist."); + excludeClasses.add("google."); + excludeClasses.add("com.jfinal."); + excludeClasses.add("com.aliyuncs."); + excludeClasses.add("com.carrotsearch."); + excludeClasses.add("org.aopalliance."); + excludeClasses.add("org.apache."); + excludeClasses.add("org.nustaq."); + excludeClasses.add("net.sf."); + excludeClasses.add("org.slf4j."); + excludeClasses.add("org.antlr."); + excludeClasses.add("org.jboss."); + excludeClasses.add("org.checkerframework."); + excludeClasses.add("org.jsoup."); + excludeClasses.add("org.objenesis."); + excludeClasses.add("org.ow2."); + excludeClasses.add("org.reactivest."); + excludeClasses.add("org.yaml."); + excludeClasses.add("org.checker"); + excludeClasses.add("org.codehaus"); + excludeClasses.add("org.commonmark"); + excludeClasses.add("org.jdom2."); + excludeClasses.add("org.aspectj."); + excludeClasses.add("org.hibernate."); + excludeClasses.add("org.ahocorasick."); + excludeClasses.add("org.lionsoul.jcseg."); + excludeClasses.add("org.ini4j."); + excludeClasses.add("org.jetbrains."); + excludeClasses.add("org.jacoco."); + excludeClasses.add("org.xnio."); + excludeClasses.add("org.bouncycastle."); + excludeClasses.add("org.elasticsearch."); + excludeClasses.add("org.hamcrest."); + excludeClasses.add("org.objectweb."); + excludeClasses.add("org.joda."); + excludeClasses.add("org.wildfly."); + excludeClasses.add("org.owasp."); + excludeClasses.add("aj.org."); + excludeClasses.add("ch.qos."); + excludeClasses.add("joptsimple."); + excludeClasses.add("com.alibaba.csp."); + excludeClasses.add("com.alibaba.nacos."); + excludeClasses.add("com.alibaba.druid."); + excludeClasses.add("com.alibaba.fastjson."); + excludeClasses.add("com.aliyun.open"); + excludeClasses.add("com.caucho"); + excludeClasses.add("com.codahale"); + excludeClasses.add("com.ctrip.framework.apollo"); + excludeClasses.add("com.ecwid."); + excludeClasses.add("com.esotericsoftware."); + excludeClasses.add("com.fasterxml."); + excludeClasses.add("com.github."); + excludeClasses.add("io.github."); + excludeClasses.add("com.google."); + excludeClasses.add("metrics_influxdb."); + excludeClasses.add("com.rabbitmq."); + excludeClasses.add("com.squareup."); + excludeClasses.add("com.sun."); + excludeClasses.add("com.typesafe."); + excludeClasses.add("com.weibo.api.motan."); + excludeClasses.add("com.zaxxer."); + excludeClasses.add("com.mysql."); + excludeClasses.add("com.papertrail."); + excludeClasses.add("com.egzosn."); + excludeClasses.add("com.alipay.api"); + excludeClasses.add("org.gjt."); + excludeClasses.add("org.fusesource."); + excludeClasses.add("org.redisson."); + excludeClasses.add("io.dropwizard"); + excludeClasses.add("io.prometheus"); + excludeClasses.add("io.jsonwebtoken"); + excludeClasses.add("io.lettuce"); + excludeClasses.add("reactor.adapter"); + excludeClasses.add("io.seata."); + excludeClasses.add("io.swagger."); + excludeClasses.add("io.undertow."); + excludeClasses.add("io.netty."); + excludeClasses.add("io.opentracing."); + excludeClasses.add("it.sauronsoftware"); + excludeClasses.add("net.oschina.j2cache"); + excludeClasses.add("net.bytebuddy"); + excludeClasses.add("cn.hutool."); + excludeClasses.add("com.dyuproject."); + excludeClasses.add("io.protostuff."); + excludeClasses.add("io.reactivex."); + excludeClasses.add("freemarker."); + excludeClasses.add("com.twelvemonkeys."); + excludeClasses.add("eu.bitwalker."); + excludeClasses.add("jstl."); + excludeClasses.add("jxl."); + excludeClasses.add("org.jxls"); + excludeClasses.add("org.kordamp"); + excludeClasses.add("org.mybatis"); + excludeClasses.add("org.lisonsoul"); + excludeClasses.add("org.flowable"); + } + + + public static void addScanClassPrefix(String prefix) { + scanClasses.add(prefix.toLowerCase().trim()); + } + + static { + scanClasses.add("io.jboot.support.shiro.directives"); + } + + static { + String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); + if (scanJarPrefix != null) { + String[] prefixes = scanJarPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addScanJarPrefix(prefix.trim()); + } + } + } + + String unScanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanJarPrefix"); + if (unScanJarPrefix != null) { + String[] prefixes = unScanJarPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addUnscanJarPrefix(prefix.trim()); + } + } + } + + String unScanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanClassPrefix"); + if (unScanClassPrefix != null) { + String[] prefixes = unScanClassPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addUnscanClassPrefix(prefix.trim()); + } + } + } + + String scanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanClassPrefix"); + if (scanClassPrefix != null) { + String[] prefixes = scanClassPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addScanClassPrefix(prefix.trim()); + } + } + } + + } + + public static List> scanSubClass(Class pclazz) { + return scanSubClass(pclazz, false); + } + + + public static List> scanSubClass(Class pclazz, boolean instantiable) { + initIfNecessary(); + List> classes = new ArrayList<>(); + findChildClasses(classes, pclazz, instantiable); + return classes; + } + + public static List scanClass() { + return scanClass(false); + } + + public static List scanClass(boolean isInstantiable) { + + initIfNecessary(); + + if (!isInstantiable) { + return new ArrayList<>(appClassesCache); + } + + return scanClass(ClassScanner::isInstantiable); + + } + + public static List scanClass(Predicate filter) { + + initIfNecessary(); + + return appClassesCache.stream() + .filter(filter) + .collect(Collectors.toList()); + + } + + public static void clearAppClassesCache() { + appClassesCache.clear(); + } + + + private static boolean isInstantiable(Class clazz) { + return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); + } + + + public static List scanClassByAnnotation(Class annotationClass, boolean instantiable) { + initIfNecessary(); + + List list = new ArrayList<>(); + for (Class clazz : appClassesCache) { + Annotation annotation = clazz.getAnnotation(annotationClass); + if (annotation == null) { + continue; + } + + if (instantiable && !isInstantiable(clazz)) { + continue; + } + + list.add(clazz); + } + + return list; + } + + private static void initIfNecessary() { + if (appClassesCache.isEmpty()) { + initAppClasses(); + } + } + + + private static void findChildClasses(List> classes, Class parent, boolean instantiable) { + for (Class clazz : appClassesCache) { + + if (!parent.isAssignableFrom(clazz)) { + continue; + } + + if (instantiable && !isInstantiable(clazz)) { + continue; + } + + classes.add(clazz); + } + } + + + private static void initAppClasses() { + + Set jarPaths = new HashSet<>(); + Set classPaths = new HashSet<>(); + + + // jdk8 及以下、 + // tomcat 容器、 + // jfinal-undertow、 + // 以上三种加载模式通过 classloader 获取 + findClassPathsAndJarsByClassloader(jarPaths, classPaths, ClassScanner.class.getClassLoader()); + + //jdk9+ 等其他方式通过 classpath 获取 + findClassPathsAndJarsByClassPath(jarPaths, classPaths); + + + String tomcatClassPath = null; + + for (String classPath : classPaths) { + //过滤tomcat自身的lib 以及 bin 下的jar + File tomcatApiJarFile = new File(classPath, "tomcat-api.jar"); + File tomcatJuliJarFile = new File(classPath, "tomcat-juli.jar"); + if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { + tomcatClassPath = tomcatApiJarFile + .getParentFile() + .getParentFile().getAbsolutePath().replace('\\', '/'); + continue; + } + + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan ClassPath: " + classPath); + } + + addClassesFromClassPath(classPath); + } + + for (String jarPath : jarPaths) { + + //过滤 tomcat 的 jar,但是不能过滤 webapps 目录下的 + if (tomcatClassPath != null + && jarPath.startsWith(tomcatClassPath) + && !jarPath.contains("webapps")) { + continue; + } + + if (!isIncludeJar(jarPath)) { + continue; + } + + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan Jar: " + jarPath); + } + + addClassesFromJar(jarPath); + } + + + } + + private static void addClassesFromJar(String jarPath) { + JarFile jarFile = null; + try { + jarFile = new JarFile(jarPath); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry jarEntry = entries.nextElement(); + if (jarEntry.isDirectory()) { + String entryName = jarEntry.getName(); + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan entryName: " + entryName); + } + + if (entryName.startsWith("BOOT-INF/classes/")) { + if (entryName.endsWith(".class")) { + String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); + addClass(classForName(className)); + } + } + } + else { + String entryName = jarEntry.getName(); + if (entryName.endsWith(".class")) { + String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); + addClass(classForName(className)); + } + else if (entryName.startsWith("BOOT-INF/lib/") && entryName.endsWith(".jar")) { + if (!isIncludeJar(entryName)) { + continue; + } + + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan Jar: " + entryName); + } + JarInputStream jarIS = new JarInputStream(jarFile + .getInputStream(jarEntry)); + + JarEntry innerEntry = jarIS.getNextJarEntry(); + while (innerEntry != null) { + if (!innerEntry.isDirectory()) { + String nestedEntryName = innerEntry.getName(); + if (nestedEntryName.endsWith(".class")) { + String className = nestedEntryName.replace("/", ".").substring(0, nestedEntryName.length() - 6); + addClass(classForName(className)); + } + } + innerEntry = jarIS.getNextJarEntry(); + } + if (jarIS != null) { + jarIS.close(); + } +// addClassesFromJar(nestedJarPath); + } + } + } + } catch (IOException e1) { + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException e) { + } + } + } + } + +// public static void main(String[] args) { +// String filePath = "D:\\test\\springbootest.jar"; +// addClassesFromJar(filePath); +// } + + private static void addClassesFromClassPath(String classPath) { + + List classFileList = new ArrayList<>(); + scanClassFile(classFileList, classPath); + + for (File file : classFileList) { + + int start = classPath.length(); + int end = file.toString().length() - ".class".length(); + + String classFile = file.toString().substring(start + 1, end); + String className = classFile.replace(File.separator, "."); + + addClass(classForName(className)); + } + } + + private static void addClass(Class clazz) { + if (clazz != null && isNotExcludeClass(clazz.getName())) { + appClassesCache.add(clazz); + } + } + + //用于在进行 fatjar 打包时,提高性能 + private static boolean isNotExcludeClass(String clazzName) { + for (String prefix : scanClasses) { + if (clazzName.startsWith(prefix)) { + return true; + } + } + for (String prefix : excludeClasses) { + if (clazzName.startsWith(prefix)) { + return false; + } + } + return true; + } + + + private static void findClassPathsAndJarsByClassloader(Set jarPaths, Set classPaths, ClassLoader classLoader) { + try { + URL[] urls = null; + if (classLoader instanceof URLClassLoader) { + URLClassLoader ucl = (URLClassLoader) classLoader; + urls = ucl.getURLs(); + } + if (urls != null) { + for (URL url : urls) { + String path = url.getPath(); + path = URLDecoder.decode(path, "UTF-8"); + + // path : /d:/xxx + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); + } + + if (!path.toLowerCase().endsWith(".jar")) { + if(path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { } + else{ + classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } else { + jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + ClassLoader parent = classLoader.getParent(); + if (parent != null) { + findClassPathsAndJarsByClassloader(jarPaths, classPaths, parent); + } + } + + private static void findClassPathsAndJarsByClassPath(Set jarPaths, Set classPaths) { + String classPath = System.getProperty("java.class.path"); + if (classPath == null || classPath.trim().length() == 0) { + return; + } + String[] classPathArray = classPath.split(File.pathSeparator); + if (classPathArray == null || classPathArray.length == 0) { + return; + } + for (String path : classPathArray) { + path = path.trim(); + + if (path.startsWith("./")) { + path = path.substring(2); + } + + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); + } + try { + if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { + if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) {} + else{ + classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } else { + jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } catch (IOException e) { + } + } + } + + + private static boolean isIncludeJar(String path) { + + String jarName = new File(path).getName().toLowerCase(); + + for (String include : scanJars) { + if (jarName.startsWith(include)) { + return true; + } + } + + for (String exclude : excludeJars) { + if (jarName.startsWith(exclude)) { + return false; + } + } + + //from jre lib + if (path.contains("/jre/lib")) { + return false; + } + + //from java home + if (getJavaHome() != null + && path.startsWith(getJavaHome())) { + return false; + } + + return true; + } + + + @SuppressWarnings("unchecked") + private static Class classForName(String className) { + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return Class.forName(className, false, cl); + } catch (Throwable ex) { + //ignore + } + return null; + } + + + private static void scanClassFile(List fileList, String path) { + File[] files = new File(path).listFiles(); + if (null == files || files.length == 0) { + return; + } + for (File file : files) { + if (file.isDirectory()) { + scanClassFile(fileList, file.getAbsolutePath()); + } else if (file.getName().endsWith(".class")) { + fileList.add(file); + } + } + } + + + private static String javaHome; + + private static String getJavaHome() { + if (javaHome == null) { + try { + String javaHomeString = System.getProperty("java.home"); + if (javaHomeString != null && javaHomeString.trim().length() > 0) { + javaHome = new File(javaHomeString, "..").getCanonicalPath().replace('\\', '/'); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return javaHome; + } + +} diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java index 9dd6788bb6495b71c2a2ef79e9ec43be2499ceeb..78f6dd8949f4344b2f13f7ffe6e1597ffd83c020 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java @@ -1,45 +1,47 @@ -package io.jboot.test.mq.redis; - - -import io.jboot.Jboot; -import io.jboot.app.JbootApplication; -import io.jboot.components.mq.MessageContext; -import io.jboot.components.mq.JbootmqMessageListener; - -public class RedisMqReceiver1 { - - public static void main(String[] args) { - - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8001"); - - //设置 mq 的相关信息 - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - - - //添加监听 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - }); - - // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - },"myChannel"); - - - //启动应用程序 - JbootApplication.run(args); - - - System.out.println("RedisMqReceiver1 started."); - } -} +package io.jboot.test.mq.redis; + + +import io.jboot.Jboot; +import io.jboot.app.JbootApplication; +import io.jboot.components.mq.MessageContext; +import io.jboot.components.mq.JbootmqMessageListener; + +public class RedisMqReceiver1 { + + public static void main(String[] args) { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8001"); + + //设置 mq 的相关信息 + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.database", 9); + JbootApplication.setBootArg("jboot.mq.redis.password", ""); + + //添加监听 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + }); + + // 只监听 myChannel 这个通道 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + },"myChannel"); + + + //启动应用程序 + JbootApplication.run(args); + Jboot.getMq().startListening(); + + System.out.println("RedisMqReceiver1 started."); + } +} diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java index 2d62864b45bab6823240430a3d581f13bb75dc5b..052e09cd294ad022cdd1dcb5d33a875d6b7c745b 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java @@ -1,47 +1,46 @@ -package io.jboot.test.mq.redis; - - -import io.jboot.Jboot; -import io.jboot.app.JbootApplication; -import io.jboot.components.mq.MessageContext; -import io.jboot.components.mq.JbootmqMessageListener; - -public class RedisMqReceiver2 { - - public static void main(String[] args) { - - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8002"); - - //设置 mq 的相关信息 - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - - - //添加监听 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - }); - - // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - },"myChannel"); - - - - //启动应用程序 - JbootApplication.run(args); - - - - System.out.println("RedisMqReceiver1 started."); - } -} +package io.jboot.test.mq.redis; + + +import io.jboot.Jboot; +import io.jboot.app.JbootApplication; +import io.jboot.components.mq.MessageContext; +import io.jboot.components.mq.JbootmqMessageListener; + +public class RedisMqReceiver2 { + + public static void main(String[] args) { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8002"); + + //设置 mq 的相关信息 + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.database", 10); + + //添加监听 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + }); + + // 只监听 myChannel 这个通道 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + },"myChannel"); + + //启动应用程序 + JbootApplication.run(args); + + Jboot.getMq().startListening(); + + System.out.println("RedisMqReceiver2 started."); + } +} diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java index e71d62961f0fc15cb71de47c0de9ccbffdf1189e..f47458f95a67cd1c4a97cd831e3b1c55a61f0528 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java @@ -1,46 +1,57 @@ -package io.jboot.test.mq.redis; - - -import io.jboot.Jboot; -import io.jboot.app.JbootApplication; - -public class RedisMqSender { - - public static void main(String[] args) throws InterruptedException { - - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8659"); - - //设置 mq 的相关信息 - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - -// JbootApplication.setBootArg("jboot.mq.other1.type", "rocketmq"); -// JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); -// JbootApplication.setBootArg("jboot.mq.other1.typeName", "rktmq"); -// JbootApplication.setBootArg("jboot.mq.rocket.rktmq.namesrvAddr", "127.0.0.1:9876"); - - //启动应用程序 - JbootApplication.run(args); - - int count = 10; - for (int i = 0; i < count; i++) { - - -// Jboot.getMq().publish("message from RedisMqSender", "channel1"); -// Jboot.getMq().publish("message from RedisMqSender", "channel2"); -// Jboot.getMq().publish("message from RedisMqSender", "myChannel"); - - Jboot.getMq().enqueue("message " + i, "channel1"); - -// Thread.sleep(2000); - System.out.println("jboot mq publish success..."); - } - - } - - -} +package io.jboot.test.mq.redis; + + +import io.jboot.Jboot; +import io.jboot.app.JbootApplication; + +public class RedisMqSender { + + public static void main(String[] args) throws InterruptedException { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8659"); + + //设置 mq 的相关信息 + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.database", 9); + JbootApplication.setBootArg("jboot.mq.redis.password", ""); + + JbootApplication.setBootArg("jboot.mq.other1.type", "redis"); + JbootApplication.setBootArg("jboot.mq.other1.typeName", "test"); + //JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.redis.test.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.test.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.test.database", 10); + JbootApplication.setBootArg("jboot.mq.redis.test.password", ""); + + //启动应用程序 + JbootApplication.run(args); + + int count = 10; + for (int i = 0; i < count; i++) { + Jboot.getMq().publish("message from RedisMqSender", "channel1"); + Jboot.getMq().publish("message from RedisMqSender", "channel2"); + Jboot.getMq().publish("message from RedisMqSender", "myChannel"); + + Jboot.getMq().enqueue("message " + i, "channel1"); + + Thread.sleep(1000); + System.out.println("jboot mq publish success..."); + } + + for (int i = 0; i < count; i++) { + Jboot.getMq("test").publish("message from RedisMqSender", "channel1"); + Jboot.getMq("test").publish("message from RedisMqSender", "channel2"); + Jboot.getMq("test").publish("message from RedisMqSender", "myChannel"); + + Jboot.getMq("test").enqueue("message " + i, "channel1"); + + Thread.sleep(1000); + System.out.println("jboot mq publish success..."); + } + } + + +}