From caeb8d0a9107a9f433a68b3b8da0878e92533da5 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Wed, 30 Aug 2023 23:18:29 +0200 Subject: [PATCH 01/43] #2676 Fixed saving a new graphical view when a validation error occurs --- src/org/scada_lts/web/mvc/controller/ViewEditController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/scada_lts/web/mvc/controller/ViewEditController.java b/src/org/scada_lts/web/mvc/controller/ViewEditController.java index 6164eff9c4..35c4bef727 100644 --- a/src/org/scada_lts/web/mvc/controller/ViewEditController.java +++ b/src/org/scada_lts/web/mvc/controller/ViewEditController.java @@ -177,9 +177,7 @@ protected ModelAndView save(HttpServletRequest request, @ModelAttribute(FORM_OBJ model.put(FORM_OBJECT_NAME, form); model.put(IMAGE_SETS_ATTRIBUTE, Common.ctx.getImageSets()); model.put(DYNAMIC_IMAGES_ATTRIBUTE, Common.ctx.getDynamicImages()); - request.getSession().removeAttribute(EMPTY_VIEW_KEY); - request.getSession().removeAttribute("view_" + view.getId()); - request.getSession().removeAttribute("view_" + view.getXid()); + model.put("currentView", view); return new ModelAndView(FORM_VIEW, model); } From c92c9d7509267239f1f5f525b5eef2973e98493e Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 01:00:53 +0200 Subject: [PATCH 02/43] #2677 Fixed longer Graphical View save time - removed cache view_by_xid; added init method for ViewCache use in MangoContextListener; added method LoggingUtils.viewInfo; refactor ViewCache; --- .../serotonin/mango/MangoContextListener.java | 1 + .../serotonin/mango/util/LoggingUtils.java | 8 +++ src/org/scada_lts/dao/DataPointDAO.java | 6 ++- src/org/scada_lts/dao/DataSourceDAO.java | 1 + src/org/scada_lts/dao/IViewDAO.java | 2 + .../dao/cache/UsersProfileCacheable.java | 21 +++----- src/org/scada_lts/dao/cache/ViewCachable.java | 21 ++++---- src/org/scada_lts/dao/cache/ViewCache.java | 33 ++++++++---- .../scada_lts/dao/cache/ViewDaoWithCache.java | 54 ++++++++++++++++--- webapp-resources/ehcache.xml | 2 - 10 files changed, 103 insertions(+), 46 deletions(-) diff --git a/src/com/serotonin/mango/MangoContextListener.java b/src/com/serotonin/mango/MangoContextListener.java index 7a42fc6118..cd2c505a3e 100644 --- a/src/com/serotonin/mango/MangoContextListener.java +++ b/src/com/serotonin/mango/MangoContextListener.java @@ -170,6 +170,7 @@ private void initialized(ServletContextEvent evt) { } try { + ApplicationBeans.getViewDaoBean().init(); ViewHierarchyCache.getInstance(); log.info("Cache views hierarchy initialized"); } catch (Exception e) { diff --git a/src/com/serotonin/mango/util/LoggingUtils.java b/src/com/serotonin/mango/util/LoggingUtils.java index 4cafc496f9..187f930dc3 100644 --- a/src/com/serotonin/mango/util/LoggingUtils.java +++ b/src/com/serotonin/mango/util/LoggingUtils.java @@ -7,6 +7,7 @@ import com.serotonin.mango.rt.dataSource.DataSourceRT; import com.serotonin.mango.rt.event.EventInstance; import com.serotonin.mango.rt.event.type.SystemEventType; +import com.serotonin.mango.view.View; import com.serotonin.mango.view.component.ScriptComponent; import com.serotonin.mango.vo.DataPointVO; import com.serotonin.mango.vo.User; @@ -189,4 +190,11 @@ public static String eventTypeInfo(int type, int alarmLevel) { String info = "event type: {0} (alarmLevel: {1})"; return MessageFormat.format(info, type, alarmLevel); } + + public static String viewInfo(View view) { + if(view == null) + return ""; + String info = "view: {0} (id: {1}, xid: {2}, userId: {3})"; + return MessageFormat.format(info, view.getName(), view.getId(), view.getXid(), view.getUserId()); + } } diff --git a/src/org/scada_lts/dao/DataPointDAO.java b/src/org/scada_lts/dao/DataPointDAO.java index b681331f0e..2883713c2d 100644 --- a/src/org/scada_lts/dao/DataPointDAO.java +++ b/src/org/scada_lts/dao/DataPointDAO.java @@ -485,7 +485,11 @@ public List selectDataPointIdentifiersWithAccess(int user } public List findIdentifiers() { - ScadaObjectIdentifierRowMapper mapper = ScadaObjectIdentifierRowMapper.withDefaultNames(); + ScadaObjectIdentifierRowMapper mapper = new ScadaObjectIdentifierRowMapper.Builder() + .nameColumnName(COLUMN_NAME_DATAPOINT_NAME) + .idColumnName(COLUMN_NAME_ID) + .xidColumnName(COLUMN_NAME_XID) + .build(); return DAO.getInstance().getJdbcTemp() .query(mapper.selectScadaObjectIdFrom(TABLE_NAME), mapper); } diff --git a/src/org/scada_lts/dao/DataSourceDAO.java b/src/org/scada_lts/dao/DataSourceDAO.java index 4d4c85ef0f..2d7da093da 100644 --- a/src/org/scada_lts/dao/DataSourceDAO.java +++ b/src/org/scada_lts/dao/DataSourceDAO.java @@ -456,6 +456,7 @@ public DataSourceVO create(DataSourceVO entity) { return entity; } + @Deprecated public List getSimpleList() { ScadaObjectIdentifierRowMapper mapper = ScadaObjectIdentifierRowMapper.withDefaultNames(); diff --git a/src/org/scada_lts/dao/IViewDAO.java b/src/org/scada_lts/dao/IViewDAO.java index 1a5f5642b9..9489656251 100644 --- a/src/org/scada_lts/dao/IViewDAO.java +++ b/src/org/scada_lts/dao/IViewDAO.java @@ -9,6 +9,8 @@ public interface IViewDAO extends ScadaRepository { + default void init() {} + View findByName(String name); @Deprecated diff --git a/src/org/scada_lts/dao/cache/UsersProfileCacheable.java b/src/org/scada_lts/dao/cache/UsersProfileCacheable.java index b9aaba5a7c..76fa675b5e 100644 --- a/src/org/scada_lts/dao/cache/UsersProfileCacheable.java +++ b/src/org/scada_lts/dao/cache/UsersProfileCacheable.java @@ -25,8 +25,7 @@ public interface UsersProfileCacheable { @CacheEvict(cacheNames = "profile_list_by_userid", key = "#p0"), @CacheEvict(cacheNames = "userid_list_by_profileid", allEntries = true), @CacheEvict(cacheNames = "share_user_list_by_watchlist", allEntries = true), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) int deleteUserProfileByUserId(int userId); @@ -34,8 +33,7 @@ public interface UsersProfileCacheable { @CacheEvict(cacheNames = "profile_list_by_userid", key = "#p0"), @CacheEvict(cacheNames = "userid_list_by_profileid", key = "#p1"), @CacheEvict(cacheNames = "share_user_list_by_watchlist", allEntries = true), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) int insertUserProfile(int userId, int profileId); @@ -49,8 +47,7 @@ public interface UsersProfileCacheable { @CacheEvict(cacheNames = "profile_list_by_userid", allEntries = true), @CacheEvict(cacheNames = "profile_list_offset_limit", allEntries = true), @CacheEvict(cacheNames = "share_user_list_by_watchlist", allEntries = true), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) int insertProfile(String profileXid, String profileName); @@ -63,8 +60,7 @@ public interface UsersProfileCacheable { @CacheEvict(cacheNames = "permission_watchlist_list_by_profile", key = "#p0"), @CacheEvict(cacheNames = "permission_view_list_by_profile", key = "#p0"), @CacheEvict(cacheNames = "share_user_list_by_watchlist", allEntries = true), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) int deleteProfile(int profileId); @@ -95,15 +91,13 @@ public interface UsersProfileCacheable { @Caching(evict = { @CacheEvict(cacheNames = "permission_view_list_by_profile", key = "#p0"), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) int[] insertViewUsersProfile(int profileId, List toInsert); @Caching(evict = { @CacheEvict(cacheNames = "permission_view_list_by_profile", key = "#p0"), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) int[] deleteViewUsersProfile(int profileId, List toDelete); @@ -147,8 +141,7 @@ default void resetCacheDataPointPermissions() {} @CacheEvict(cacheNames = "profile_list_by_userid", allEntries = true), @CacheEvict(cacheNames = "permission_view_list_by_profile", allEntries = true), @CacheEvict(cacheNames = "permission_view_list_by_user", allEntries = true), - @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true), - @CacheEvict(cacheNames = "view_list", allEntries = true) + @CacheEvict(cacheNames = "share_user_list_by_view", allEntries = true) }) default void resetCacheViewPermissions() {} diff --git a/src/org/scada_lts/dao/cache/ViewCachable.java b/src/org/scada_lts/dao/cache/ViewCachable.java index 09ef0d483d..aa674723d6 100644 --- a/src/org/scada_lts/dao/cache/ViewCachable.java +++ b/src/org/scada_lts/dao/cache/ViewCachable.java @@ -3,6 +3,7 @@ import br.org.scadabr.vo.permission.ViewAccess; import com.serotonin.mango.view.ShareUser; import com.serotonin.mango.view.View; +import org.scada_lts.dao.model.ScadaObjectIdentifier; import org.springframework.cache.annotation.*; import java.util.List; @@ -14,7 +15,6 @@ public interface ViewCachable { @Caching(evict = { @CacheEvict(cacheNames = "view_list", allEntries = true), @CacheEvict(cacheNames = "view_by_id", key = "#view.id"), - @CacheEvict(cacheNames = "view_by_xid", key = "#view.xid"), @CacheEvict(cacheNames = "permission_view_list_by_user", allEntries = true), @CacheEvict(cacheNames = "share_user_list_by_view", key = "'shareUsersFromProfile' + #view.id"), @CacheEvict(cacheNames = "share_user_list_by_view", key = "'shareUsers' + #view.id") @@ -24,7 +24,6 @@ public interface ViewCachable { @Caching(evict = { @CacheEvict(cacheNames = "view_list", allEntries = true), @CacheEvict(cacheNames = "view_by_id", key = "#p0"), - @CacheEvict(cacheNames = "view_by_xid", allEntries = true), @CacheEvict(cacheNames = "permission_view_list_by_user", allEntries = true), @CacheEvict(cacheNames = "share_user_list_by_view", key = "'shareUsersFromProfile' + #p0"), @CacheEvict(cacheNames = "share_user_list_by_view", key = "'shareUsers' + #p0") @@ -32,9 +31,7 @@ public interface ViewCachable { void delete(int viewId); @Caching(evict = { - @CacheEvict(cacheNames = "view_list", allEntries = true), @CacheEvict(cacheNames = "view_by_id", key = "#view.id"), - @CacheEvict(cacheNames = "view_by_xid", key = "#view.xid"), @CacheEvict(cacheNames = "permission_view_list_by_user", allEntries = true), @CacheEvict(cacheNames = "share_user_list_by_view", key = "'shareUsersFromProfile' + #view.id"), @CacheEvict(cacheNames = "share_user_list_by_view", key = "'shareUsers' + #view.id") @@ -42,7 +39,7 @@ public interface ViewCachable { void update(View view); @Cacheable(cacheNames = "view_list", key = "'views'") - List findAll(); + List findIdentifiers(); @Caching(evict = { @CacheEvict(cacheNames = "permission_view_list_by_user", allEntries = true), @@ -59,7 +56,7 @@ public interface ViewCachable { }) void deleteViewForUser(int viewId, int userId); - @Cacheable(cacheNames = "permission_view_list_by_user", key = "#p0", condition = "#userId != 0") + @Cacheable(cacheNames = "permission_view_list_by_user", key = "#p0", condition = "#p0 > -1") List selectViewPermissions(int userId); @Caching(evict = { @@ -75,15 +72,17 @@ public interface ViewCachable { int[] deletePermissions(int userId, List toRemove); - @Cacheable(cacheNames = "share_user_list_by_view", key = "'shareUsers' + #p0", condition = "#viewId != 0") + @Cacheable(cacheNames = "share_user_list_by_view", key = "'shareUsers' + #p0", condition = "#p0 > 0") List selectShareUsers(int viewId); - @Cacheable(cacheNames = "share_user_list_by_view", key = "'shareUsersFromProfile' + #p0", condition = "#viewId != 0") + @Cacheable(cacheNames = "share_user_list_by_view", key = "'shareUsersFromProfile' + #p0", condition = "#p0 > 0") List selectShareUsersFromProfile(int viewId); - @Cacheable(cacheNames = "view_by_id", key = "#p0", condition = "#viewId != 0", unless = "#result == null") + @Cacheable(cacheNames = "view_by_id", key = "#p0", condition = "#p0 > 0") View findById(int viewId); - @Cacheable(cacheNames = "view_by_xid", key = "#viewXid", condition = "#viewXid != null", unless = "#result == null") - View findByXid(String viewXid); + @CachePut(cacheNames = "view_by_id", key = "#view.id", condition = "#view != null && #view.id > 0") + default View put(View view) { + return view; + } } diff --git a/src/org/scada_lts/dao/cache/ViewCache.java b/src/org/scada_lts/dao/cache/ViewCache.java index 37dcbab182..4268d9c1a5 100644 --- a/src/org/scada_lts/dao/cache/ViewCache.java +++ b/src/org/scada_lts/dao/cache/ViewCache.java @@ -1,14 +1,18 @@ package org.scada_lts.dao.cache; import br.org.scadabr.vo.permission.ViewAccess; +import com.serotonin.mango.util.LoggingUtils; import com.serotonin.mango.view.ShareUser; import com.serotonin.mango.view.View; +import org.apache.commons.logging.LogFactory; import org.scada_lts.dao.ViewDAO; +import org.scada_lts.dao.model.ScadaObjectIdentifier; import java.util.List; public class ViewCache implements ViewCachable { + private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(ViewCache.class); private final ViewDAO viewDAO; public ViewCache(ViewDAO viewDAO) { @@ -16,63 +20,72 @@ public ViewCache(ViewDAO viewDAO) { } @Override - public List findAll() { - return viewDAO.findAll(); + public List findIdentifiers() { + LOG.info("no cache: findAll"); + return viewDAO.findIdentifiers(); } @Override public View save(View view) { + LOG.info("no cache: " + LoggingUtils.viewInfo(view)); return viewDAO.save(view); } @Override public void update(View view) { + LOG.info("no cache: " + LoggingUtils.viewInfo(view)); viewDAO.update(view); } @Override - public void delete(int id) { - viewDAO.delete(id); + public void delete(int viewId) { + LOG.info("no cache: viewId: " + viewId); + viewDAO.delete(viewId); } @Override public List selectShareUsers(int viewId) { + LOG.info("no cache: viewId: " + viewId); return viewDAO.selectShareUsers(viewId); } @Override public List selectShareUsersFromProfile(int viewId) { + LOG.info("no cache: viewId: " + viewId); return viewDAO.selectShareUsersFromProfile(viewId); } @Override public List selectViewPermissions(int userId) { + LOG.info("no cache: userId: " + userId); return viewDAO.selectViewPermissions(userId); } @Override public int[] insertPermissions(int userId, List toAddOrUpdate) { + LOG.info("no cache: userId: " + userId + ", toAddOrUpdate: " + toAddOrUpdate); return viewDAO.insertPermissions(userId, toAddOrUpdate); } @Override public int[] deletePermissions(int userId, List toRemove) { + LOG.info("no cache: userId: " + userId + ", toAddOrUpdate: " + toRemove); return viewDAO.deletePermissions(userId, toRemove); } @Override public View findById(int viewId) { + LOG.info("no cache: viewId: " + viewId); return viewDAO.findById(viewId); } @Override - public View findByXid(String viewXid) { - return viewDAO.findByXid(viewXid); + public void deleteViewForUser(int viewId) { + LOG.info("no cache: viewId: " + viewId); } @Override - public void deleteViewForUser(int viewId) {} - - @Override - public void deleteViewForUser(int viewId, int userId) {} + public void deleteViewForUser(int viewId, int userId) { + LOG.info("no cache: viewId: " + viewId + ", userId: " + userId); + } } diff --git a/src/org/scada_lts/dao/cache/ViewDaoWithCache.java b/src/org/scada_lts/dao/cache/ViewDaoWithCache.java index 3655a87696..eed9d93c65 100644 --- a/src/org/scada_lts/dao/cache/ViewDaoWithCache.java +++ b/src/org/scada_lts/dao/cache/ViewDaoWithCache.java @@ -1,13 +1,17 @@ package org.scada_lts.dao.cache; import br.org.scadabr.vo.permission.ViewAccess; +import com.serotonin.mango.Common; import com.serotonin.mango.view.ShareUser; import com.serotonin.mango.view.View; import com.serotonin.mango.vo.User; import org.scada_lts.dao.IViewDAO; +import org.scada_lts.dao.ViewDAO; import org.scada_lts.dao.model.ScadaObjectIdentifier; import org.scada_lts.permissions.service.GetViewsWithAccess; +import org.scada_lts.permissions.service.ViewGetShareUsers; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -19,6 +23,15 @@ public ViewDaoWithCache(ViewCachable viewCache) { this.viewCache = viewCache; } + @Override + public void init() { + List views = new ViewDAO().findAll(); + for(View view: views) { + applyShareUsers(view); + viewCache.put(view); + } + } + @Override public View save(View view) { return viewCache.save(view); @@ -36,7 +49,14 @@ public void delete(Integer id) { @Override public List findAll() { - return viewCache.findAll(); + List identifiers = viewCache.findIdentifiers(); + List views = new ArrayList<>(); + for(ScadaObjectIdentifier identifier: identifiers) { + View view = viewCache.findById(identifier.getId()); + applyShareUsers(view); + views.add(view); + } + return views; } @Override @@ -65,25 +85,38 @@ public List selectShareUsersFromProfile(int viewId) { } @Override - public View findById(Integer id) { - return viewCache.findById(id); + public View findById(Integer viewId) { + if(viewId == null || viewId == Common.NEW_ID || viewId == 0) { + return null; + } + return viewCache.findById(viewId); } @Override public View findByName(String name) { - return findAll().stream().filter(a -> a.getName().equals(name)).findAny().orElse(null); + if(name == null) { + return null; + } + return findAll().stream() + .filter(a -> a.getName() != null) + .filter(a -> a.getName().equals(name)) + .findAny().orElse(null); } @Override public View findByXid(String xid) { - return viewCache.findByXid(xid); + if(xid == null) { + return null; + } + return findAll().stream() + .filter(a -> a.getXid() != null) + .filter(a -> a.getXid().equals(xid)) + .findAny().orElse(null); } @Override public List findIdentifiers() { - return findAll().stream() - .map(a -> new ScadaObjectIdentifier(a.getId(), a.getXid(), a.getName())) - .collect(Collectors.toList()); + return viewCache.findIdentifiers(); } @Override @@ -109,4 +142,9 @@ public void deleteViewForUser(int viewId) {} public void deleteViewForUser(int viewId, int userId) { viewCache.deleteViewForUser(viewId, userId); } + + private void applyShareUsers(View view) { + ViewGetShareUsers viewGetShareUsers = new ViewGetShareUsers(this); + view.setViewUsers(viewGetShareUsers.getShareUsersWithProfile(view)); + } } diff --git a/webapp-resources/ehcache.xml b/webapp-resources/ehcache.xml index 15a04f828a..cde26c2d30 100644 --- a/webapp-resources/ehcache.xml +++ b/webapp-resources/ehcache.xml @@ -159,9 +159,7 @@ - - From 0ac8ae244a62cff985a04271c9198210ae1e54fb Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 01:12:53 +0200 Subject: [PATCH 03/43] #2674 Corrected configuration script security context - Less restrictive configuration for the key: scadalts.security.js.access.granted.class.regexes; refactoring SandboxContextFactory and SandboxNativeJavaObject --- .../scripting/SandboxContextFactory.java | 22 +++++++++++++++---- .../scripting/SandboxNativeJavaObject.java | 22 +++++++++++++++---- webapp-resources/env.properties | 2 +- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/org/scada_lts/scripting/SandboxContextFactory.java b/src/org/scada_lts/scripting/SandboxContextFactory.java index 937fe907d3..6a69436d3e 100644 --- a/src/org/scada_lts/scripting/SandboxContextFactory.java +++ b/src/org/scada_lts/scripting/SandboxContextFactory.java @@ -24,6 +24,10 @@ import org.mozilla.javascript.ContextFactory; import org.scada_lts.utils.SystemSettingsUtils; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Set new protected context * @@ -32,21 +36,31 @@ public class SandboxContextFactory extends ContextFactory { private static final Log LOG = LogFactory.getLog(SandboxContextFactory.class); + + private static final Pattern[] SECURITY_JS_ACCESS_DENIED_CLASS_REGEXES = Stream.of(SystemSettingsUtils.getSecurityJsAccessDeniedClassRegexes()) + .map(Pattern::compile) + .collect(Collectors.toList()) + .toArray(new Pattern[]{}); + + private static final Pattern[] SECURITY_JS_ACCESS_GRANTED_CLASS_REGEXES = Stream.of(SystemSettingsUtils.getSecurityJsAccessGrantedClassRegexes()) + .map(Pattern::compile) + .collect(Collectors.toList()) + .toArray(new Pattern[]{}); @Override protected Context makeContext() { Context cx = super.makeContext(); cx.setWrapFactory(new SandboxWrapFactory()); cx.setClassShutter(className -> { - for(String classNameRegex: SystemSettingsUtils.getSecurityJsAccessDeniedClassRegexes()) { - if (className.matches(classNameRegex)) { + for(Pattern pattern: SECURITY_JS_ACCESS_DENIED_CLASS_REGEXES) { + if (pattern.matcher(className).matches()) { if(LOG.isWarnEnabled()) LOG.warn("access denied for class: " + className); return false; } } - for(String classNameRegex: SystemSettingsUtils.getSecurityJsAccessGrantedClassRegexes()) { - if (className.matches(classNameRegex)) { + for(Pattern pattern: SECURITY_JS_ACCESS_GRANTED_CLASS_REGEXES) { + if (pattern.matcher(className).matches()) { return true; } } diff --git a/src/org/scada_lts/scripting/SandboxNativeJavaObject.java b/src/org/scada_lts/scripting/SandboxNativeJavaObject.java index 02ee886f9d..a1cc925760 100644 --- a/src/org/scada_lts/scripting/SandboxNativeJavaObject.java +++ b/src/org/scada_lts/scripting/SandboxNativeJavaObject.java @@ -24,6 +24,10 @@ import org.mozilla.javascript.Scriptable; import org.scada_lts.utils.SystemSettingsUtils; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Own sandbox for native java object. * @@ -34,21 +38,31 @@ public class SandboxNativeJavaObject extends NativeJavaObject { private static final long serialVersionUID = -7251515169121482423L; private static final Log LOG = LogFactory.getLog(SandboxNativeJavaObject.class); + private static final Pattern[] SECURITY_JS_ACCESS_DENIED_METHOD_REGEXES = Stream.of(SystemSettingsUtils.getSecurityJsAccessDeniedMethodRegexes()) + .map(Pattern::compile) + .collect(Collectors.toList()) + .toArray(new Pattern[]{}); + + private static final Pattern[] SECURITY_JS_ACCESS_GRANTED_METHOD_REGEXES = Stream.of(SystemSettingsUtils.getSecurityJsAccessGrantedMethodRegexes()) + .map(Pattern::compile) + .collect(Collectors.toList()) + .toArray(new Pattern[]{}); + public SandboxNativeJavaObject(Scriptable scope, Object javaObject, @SuppressWarnings("rawtypes") Class staticType) { super(scope, javaObject, staticType); } @Override public Object get(String methodName, Scriptable start) { - for(String methodNameRegex: SystemSettingsUtils.getSecurityJsAccessDeniedMethodRegexes()) { - if (methodName.matches(methodNameRegex)) { + for(Pattern pattern: SECURITY_JS_ACCESS_DENIED_METHOD_REGEXES) { + if (pattern.matcher(methodName).matches()) { if(LOG.isWarnEnabled()) LOG.warn("access denied for method: " + methodName); return NOT_FOUND; } } - for(String methodNameRegex: SystemSettingsUtils.getSecurityJsAccessGrantedMethodRegexes()) { - if (methodName.matches(methodNameRegex)) { + for(Pattern pattern: SECURITY_JS_ACCESS_GRANTED_METHOD_REGEXES) { + if (pattern.matcher(methodName).matches()) { return super.get(methodName, start); } } diff --git a/webapp-resources/env.properties b/webapp-resources/env.properties index f51dc522d5..70ca032a42 100644 --- a/webapp-resources/env.properties +++ b/webapp-resources/env.properties @@ -126,7 +126,7 @@ processing.workitems.history.longer.limit=200 scadalts.security.js.access.denied.method.regexes=getDeclaredField;getPassword;getClass;getRuntime;exec scadalts.security.js.access.denied.class.regexes=java.lang.Runtime;java.lang.Runtime.*;java.lang.reflect.*;java.lang.ref.*;java.lang.System.*;java.lang.System;java.lang.ClassLoader;java.lang.Compiler;java.lang.SecurityManager;java.lang.Thread;java.lang.ThreadGroup;java.lang.ThreadLocal scadalts.security.js.access.granted.method.regexes=^.* -scadalts.security.js.access.granted.class.regexes=java.lang.*;java.util.*;java.math.*;java.text.*;java.time.*;br.org.scadabr.*;cc.radiuino.scadabr.*;com.serotonin.*;org.scada_lts.* +scadalts.security.js.access.granted.class.regexes=^.* view.forceFullScreen=false view.hideShortcutDisableFullScreen=false From 3d10bb52f398d20f5c0a244bb5d58916f99cfa57 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 01:22:13 +0200 Subject: [PATCH 04/43] #2673 Fixed unavailability of acknowledge for active event - hidden acknowledge button for event active in "Pending alarms"/"Event search"; corrected method acknowledgeEvent and acknowledgeAllPendingEvents - acknowledge if event not active --- WebContent/WEB-INF/tags/alarmAck.tag | 2 +- src/com/serotonin/mango/web/dwr/MiscDwr.java | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/WebContent/WEB-INF/tags/alarmAck.tag b/WebContent/WEB-INF/tags/alarmAck.tag index 62f8bee706..4216d3ed2b 100644 --- a/WebContent/WEB-INF/tags/alarmAck.tag +++ b/WebContent/WEB-INF/tags/alarmAck.tag @@ -22,7 +22,7 @@ - + diff --git a/src/com/serotonin/mango/web/dwr/MiscDwr.java b/src/com/serotonin/mango/web/dwr/MiscDwr.java index 705a3cd1f3..4ccc2aaae5 100644 --- a/src/com/serotonin/mango/web/dwr/MiscDwr.java +++ b/src/com/serotonin/mango/web/dwr/MiscDwr.java @@ -121,9 +121,12 @@ public int acknowledgeEvent(int eventId) { User user = Common.getUser(); MangoEvent eventService = new EventService(); if (user != null) { - eventService.ackEvent(eventId, System.currentTimeMillis(), - user.getId(), 0); - resetLastAlarmLevelChange(); + EventInstance evt = eventService.getEvent(eventId); + if(evt != null && !evt.isActive()) { + eventService.ackEvent(evt.getId(), System.currentTimeMillis(), + user.getId(), 0); + resetLastAlarmLevelChange(); + } } return eventId; } @@ -133,8 +136,10 @@ public void acknowledgeAllPendingEvents() { if (user != null) { MangoEvent eventService = new EventService(); long now = System.currentTimeMillis(); - for (EventInstance evt : eventService.getPendingEvents(user.getId())) - eventService.ackEvent(evt.getId(), now, user.getId(), 0); + for (EventInstance evt : eventService.getPendingEvents(user.getId())) { + if(!evt.isActive()) + eventService.ackEvent(evt.getId(), now, user.getId(), 0); + } resetLastAlarmLevelChange(); } } From 797270a5fbe2f2b46c3659c6871db70262ac5581 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 09:24:06 +0200 Subject: [PATCH 05/43] #2675 Fixed longer time startup application - corrected limit cache in ehcache.xml; added init method for PointEventDetectorCache use MangoContextListener; added method PointEventDetectorDAO.getPointEventDetectors --- .../serotonin/mango/MangoContextListener.java | 9 +++- src/org/scada_lts/dao/DataPointDAO.java | 6 ++- .../scada_lts/dao/IPointEventDetectorDAO.java | 1 + .../scada_lts/dao/PointEventDetectorDAO.java | 47 +++++++++++++++++++ .../dao/cache/PointEventDetectorCache.java | 10 ++++ .../cache/PointEventDetectorCacheable.java | 6 +++ .../cache/PointEventDetectorDaoWithCache.java | 23 +++++++++ webapp-resources/ehcache.xml | 16 +++++-- 8 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/com/serotonin/mango/MangoContextListener.java b/src/com/serotonin/mango/MangoContextListener.java index 7a42fc6118..c5727edad3 100644 --- a/src/com/serotonin/mango/MangoContextListener.java +++ b/src/com/serotonin/mango/MangoContextListener.java @@ -135,7 +135,14 @@ private void initialized(ServletContextEvent evt) { utilitiesInitialize(ctx); eventManagerInitialize(ctx); - + + try { + ApplicationBeans.getPointEventDetectorDaoBean().init(); + log.info("Cache event detectors initialized"); + } catch (Exception e) { + log.error(e); + } + try { DataSourcePointsCache.getInstance().cacheInitialize(); log.info("Cache data points initialized"); diff --git a/src/org/scada_lts/dao/DataPointDAO.java b/src/org/scada_lts/dao/DataPointDAO.java index b681331f0e..2883713c2d 100644 --- a/src/org/scada_lts/dao/DataPointDAO.java +++ b/src/org/scada_lts/dao/DataPointDAO.java @@ -485,7 +485,11 @@ public List selectDataPointIdentifiersWithAccess(int user } public List findIdentifiers() { - ScadaObjectIdentifierRowMapper mapper = ScadaObjectIdentifierRowMapper.withDefaultNames(); + ScadaObjectIdentifierRowMapper mapper = new ScadaObjectIdentifierRowMapper.Builder() + .nameColumnName(COLUMN_NAME_DATAPOINT_NAME) + .idColumnName(COLUMN_NAME_ID) + .xidColumnName(COLUMN_NAME_XID) + .build(); return DAO.getInstance().getJdbcTemp() .query(mapper.selectScadaObjectIdFrom(TABLE_NAME), mapper); } diff --git a/src/org/scada_lts/dao/IPointEventDetectorDAO.java b/src/org/scada_lts/dao/IPointEventDetectorDAO.java index f0425dccb9..c641011285 100644 --- a/src/org/scada_lts/dao/IPointEventDetectorDAO.java +++ b/src/org/scada_lts/dao/IPointEventDetectorDAO.java @@ -6,6 +6,7 @@ import java.util.List; public interface IPointEventDetectorDAO { + default void init() {}; List getPointEventDetectors(DataPointVO dataPoint); boolean isEventDetectorXidUnique(int dataPointId, String xid, int excludeId); int insert(int dataPointId, PointEventDetectorVO pointEventDetector); diff --git a/src/org/scada_lts/dao/PointEventDetectorDAO.java b/src/org/scada_lts/dao/PointEventDetectorDAO.java index 69cdf41ffd..1b4c0b6572 100644 --- a/src/org/scada_lts/dao/PointEventDetectorDAO.java +++ b/src/org/scada_lts/dao/PointEventDetectorDAO.java @@ -21,6 +21,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.List; import com.serotonin.mango.Common; @@ -85,6 +86,7 @@ public class PointEventDetectorDAO implements IPointEventDetectorDAO { + COLUMN_NAME_MULTISTATE_STATE + ", " + COLUMN_NAME_CHANGE_COUNT + ", " + COLUMN_NAME_ALPHANUMERIC_STATE + ", " + + COLUMN_NAME_DATA_POINT_ID + ", " + COLUMN_NAME_WEIGHT + " " + "from pointEventDetectors "; @@ -208,6 +210,31 @@ public PointEventDetectorVO mapRow(ResultSet rs, int rowNum) throws SQLException } } + private class PointEventDetectorRowMapper2 implements RowMapper { + + @Override + public PointEventDetectorVO mapRow(ResultSet rs, int rowNum) throws SQLException { + PointEventDetectorVO pointEventDetector = new PointEventDetectorVO(); + pointEventDetector.setId(rs.getInt(COLUMN_NAME_ID)); + pointEventDetector.setXid(rs.getString(COLUMN_NAME_XID)); + pointEventDetector.setAlias(rs.getString(COLUMN_NAME_ALIAS)); + pointEventDetector.setDetectorType(rs.getInt(COLUMN_NAME_DETECTOR_TYPE)); + pointEventDetector.setAlarmLevel(rs.getInt(COLUMN_NAME_ALARM_LEVEL)); + pointEventDetector.setLimit(rs.getDouble(COLUMN_NAME_STATE_LIMIT)); + pointEventDetector.setDuration(rs.getInt(COLUMN_NAME_DURATION)); + pointEventDetector.setDurationType(rs.getInt(COLUMN_NAME_DURATION_TYPE)); + pointEventDetector.setBinaryState(DAO.charToBool(rs.getString(COLUMN_NAME_BINARY_STATE))); + pointEventDetector.setMultistateState(rs.getInt(COLUMN_NAME_MULTISTATE_STATE)); + pointEventDetector.setChangeCount(rs.getInt(COLUMN_NAME_CHANGE_COUNT)); + pointEventDetector.setAlphanumericState(rs.getString(COLUMN_NAME_ALPHANUMERIC_STATE)); + pointEventDetector.setWeight(rs.getDouble(COLUMN_NAME_WEIGHT)); + DataPointVO dataPointVO = new DataPointVO(DataPointVO.LoggingTypes.ON_CHANGE); + dataPointVO.setId(rs.getInt(COLUMN_NAME_DATA_POINT_ID)); + pointEventDetector.njbSetDataPoint(dataPointVO); + return pointEventDetector; + } + } + @Override public int getDataPointId(int pointEventDetectorId) { @@ -446,4 +473,24 @@ public PointEventDetectorVO getPointEventDetector(String pointEventDetectorXid, return new PointEventDetectorVO(Common.NEW_ID, null); } } + + public List getPointEventDetectors(long limit, int offset) { + if (LOG.isTraceEnabled()) { + LOG.trace("getPointEventDetector(long limit, int offset) limit:" +limit + ", offset:" + offset); + } + + String templateSelectWhereIdOrderBy = POINT_EVENT_DETECTOR_SELECT + "order by " + COLUMN_NAME_ID + " LIMIT ? OFFSET ?"; + + try { + return DAO.getInstance().getJdbcTemp().query(templateSelectWhereIdOrderBy, new Object[]{limit, offset}, new PointEventDetectorRowMapper2()); + } catch (EmptyResultDataAccessException ex) { + return Collections.emptyList(); + } catch (IncorrectResultSizeDataAccessException ex) { + LOG.warn("There is more than one detector with limit:" + limit + ", offset:" + offset + ", msg: " +ex.getMessage(), ex); + return Collections.emptyList(); + } catch (Exception ex) { + LOG.warn(ex.getMessage(), ex); + return Collections.emptyList(); + } + } } diff --git a/src/org/scada_lts/dao/cache/PointEventDetectorCache.java b/src/org/scada_lts/dao/cache/PointEventDetectorCache.java index 4b02b61bcf..803095333a 100644 --- a/src/org/scada_lts/dao/cache/PointEventDetectorCache.java +++ b/src/org/scada_lts/dao/cache/PointEventDetectorCache.java @@ -1,5 +1,6 @@ package org.scada_lts.dao.cache; +import com.serotonin.mango.util.LoggingUtils; import com.serotonin.mango.vo.DataPointVO; import com.serotonin.mango.vo.event.PointEventDetectorVO; import org.apache.commons.logging.LogFactory; @@ -19,46 +20,55 @@ public PointEventDetectorCache(PointEventDetectorDAO pointEventDetectorDAO) { @Override public int insert(int dataPointId, PointEventDetectorVO pointEventDetector) { + LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); return pointEventDetectorDAO.insert(dataPointId, pointEventDetector); } @Override public void update(int dataPointId, PointEventDetectorVO pointEventDetector) { + LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); pointEventDetectorDAO.update(dataPointId, pointEventDetector); } @Override public void updateWithType(int dataPointId, PointEventDetectorVO pointEventDetector) { + LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); pointEventDetectorDAO.updateWithType(dataPointId, pointEventDetector); } @Override public List selectPointEventDetectors(DataPointVO dataPoint) { + LOG.error("no cache: " + LoggingUtils.dataPointInfo(dataPoint)); return pointEventDetectorDAO.getPointEventDetectors(dataPoint); } @Override public void delete(int dataPointId, PointEventDetectorVO pointEventDetector) { + LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); pointEventDetectorDAO.delete(dataPointId, pointEventDetector); } @Override public void deleteWithId(String dataPointIds) { + LOG.error("no cache: dataPointIds: " + dataPointIds); pointEventDetectorDAO.deleteWithId(dataPointIds); } @Override public PointEventDetectorVO selectPointEventDetector(int pointEventDetectorId) { + LOG.error("no cache: pointEventDetectorId: " + pointEventDetectorId); return pointEventDetectorDAO.getPointEventDetector(pointEventDetectorId); } @Override public PointEventDetectorVO selectPointEventDetector(String pointEventDetectorXid, int dataPointId) { + LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetectorXid: " + pointEventDetectorXid); return pointEventDetectorDAO.getPointEventDetector(pointEventDetectorXid, dataPointId); } @Override public int selectDataPointIdByEventDetectorId(int pointEventDetectorId) { + LOG.error("no cache: pointEventDetectorId: " + pointEventDetectorId); return pointEventDetectorDAO.getDataPointId(pointEventDetectorId); } } diff --git a/src/org/scada_lts/dao/cache/PointEventDetectorCacheable.java b/src/org/scada_lts/dao/cache/PointEventDetectorCacheable.java index fb2e364b11..a661722b6e 100644 --- a/src/org/scada_lts/dao/cache/PointEventDetectorCacheable.java +++ b/src/org/scada_lts/dao/cache/PointEventDetectorCacheable.java @@ -3,6 +3,7 @@ import com.serotonin.mango.vo.DataPointVO; import com.serotonin.mango.vo.event.PointEventDetectorVO; import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; @@ -85,4 +86,9 @@ public interface PointEventDetectorCacheable { @CacheEvict(cacheNames = "data_point_id_by_point_event_detector_id", allEntries = true) }) default void deleteWithId(String dataPointIds) {} + + @CachePut(cacheNames = "point_event_detector_list_by_data_point_id", key = "#p0", condition = "#p0 != -1 && #p1 != null") + default List put(int dataPointId, List detectors) { + return detectors; + } } diff --git a/src/org/scada_lts/dao/cache/PointEventDetectorDaoWithCache.java b/src/org/scada_lts/dao/cache/PointEventDetectorDaoWithCache.java index 80ce7acb72..0f2d1c7c7c 100644 --- a/src/org/scada_lts/dao/cache/PointEventDetectorDaoWithCache.java +++ b/src/org/scada_lts/dao/cache/PointEventDetectorDaoWithCache.java @@ -3,9 +3,13 @@ import com.serotonin.mango.Common; import com.serotonin.mango.vo.DataPointVO; import com.serotonin.mango.vo.event.PointEventDetectorVO; +import org.scada_lts.dao.DataPointDAO; import org.scada_lts.dao.IPointEventDetectorDAO; +import org.scada_lts.dao.PointEventDetectorDAO; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; public class PointEventDetectorDaoWithCache implements IPointEventDetectorDAO { @@ -16,6 +20,25 @@ public PointEventDetectorDaoWithCache(PointEventDetectorCacheable pointEventDete this.pointEventDetectorCache = pointEventDetectorCache; } + @Override + public void init() { + List dataPoints = new DataPointDAO().getDataPoints(); + Map> pointEventDetectors = new PointEventDetectorDAO() + .getPointEventDetectors(Integer.MAX_VALUE, 0) + .stream() + .collect(Collectors.groupingBy(a -> a.njbGetDataPoint().getId())); + for(DataPointVO dataPoint: dataPoints) { + List detectors = pointEventDetectors.get(dataPoint.getId()); + if(detectors != null && !detectors.isEmpty()) { + dataPoint.setEventDetectors(detectors); + for(PointEventDetectorVO detector: detectors) { + detector.njbSetDataPoint(dataPoint); + } + } + pointEventDetectorCache.put(dataPoint.getId(), detectors == null ? new ArrayList<>() : detectors); + } + } + @Override public List getPointEventDetectors(DataPointVO dataPoint) { return pointEventDetectorCache.selectPointEventDetectors(dataPoint).stream() diff --git a/webapp-resources/ehcache.xml b/webapp-resources/ehcache.xml index 15a04f828a..deb29039ff 100644 --- a/webapp-resources/ehcache.xml +++ b/webapp-resources/ehcache.xml @@ -85,7 +85,9 @@ - 200 + + 100 + @@ -94,7 +96,9 @@ - 200 + + 100 + @@ -103,7 +107,9 @@ - 200 + + 100 + @@ -112,7 +118,9 @@ - 200 + + 100 + From 9afcb81ebe97fe6c9125fd813ce42b7b2e3221e0 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 09:43:13 +0200 Subject: [PATCH 06/43] #2677 Fixed longer Graphical View save time - revert DataPointDAO --- src/org/scada_lts/dao/DataPointDAO.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/org/scada_lts/dao/DataPointDAO.java b/src/org/scada_lts/dao/DataPointDAO.java index 2883713c2d..b681331f0e 100644 --- a/src/org/scada_lts/dao/DataPointDAO.java +++ b/src/org/scada_lts/dao/DataPointDAO.java @@ -485,11 +485,7 @@ public List selectDataPointIdentifiersWithAccess(int user } public List findIdentifiers() { - ScadaObjectIdentifierRowMapper mapper = new ScadaObjectIdentifierRowMapper.Builder() - .nameColumnName(COLUMN_NAME_DATAPOINT_NAME) - .idColumnName(COLUMN_NAME_ID) - .xidColumnName(COLUMN_NAME_XID) - .build(); + ScadaObjectIdentifierRowMapper mapper = ScadaObjectIdentifierRowMapper.withDefaultNames(); return DAO.getInstance().getJdbcTemp() .query(mapper.selectScadaObjectIdFrom(TABLE_NAME), mapper); } From a724caa15d70ca811feaa058ab82eca5a51abe58 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 09:49:57 +0200 Subject: [PATCH 07/43] #2675 Fixed longer time startup application - removed deprecated method: DataPointDAO.getSimpleList; removed method: DataPointDAO.getAll; --- src/org/scada_lts/dao/DataPointDAO.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/org/scada_lts/dao/DataPointDAO.java b/src/org/scada_lts/dao/DataPointDAO.java index 2883713c2d..9f2f2814aa 100644 --- a/src/org/scada_lts/dao/DataPointDAO.java +++ b/src/org/scada_lts/dao/DataPointDAO.java @@ -360,15 +360,6 @@ public DataPointVO create(DataPointVO entity) { return entity; } - @Deprecated - public List getSimpleList() { - return findIdentifiers(); - } - - public List getAll() { - return null; - } - public DataPointVO getById(int id) throws EmptyResultDataAccessException { return getDataPoint(id); } From eb3f7f4cec208eb1565caf995504e6ba07b659a7 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 10:19:04 +0200 Subject: [PATCH 08/43] #2675 Fixed longer time startup application - rename class PointEventDetectorDAO.PointEventDetectorDataPointIdRowMapper --- src/org/scada_lts/dao/PointEventDetectorDAO.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/scada_lts/dao/PointEventDetectorDAO.java b/src/org/scada_lts/dao/PointEventDetectorDAO.java index 1b4c0b6572..e4c1309c72 100644 --- a/src/org/scada_lts/dao/PointEventDetectorDAO.java +++ b/src/org/scada_lts/dao/PointEventDetectorDAO.java @@ -210,7 +210,7 @@ public PointEventDetectorVO mapRow(ResultSet rs, int rowNum) throws SQLException } } - private class PointEventDetectorRowMapper2 implements RowMapper { + private class PointEventDetectorDataPointIdRowMapper implements RowMapper { @Override public PointEventDetectorVO mapRow(ResultSet rs, int rowNum) throws SQLException { @@ -482,7 +482,7 @@ public List getPointEventDetectors(long limit, int offset) String templateSelectWhereIdOrderBy = POINT_EVENT_DETECTOR_SELECT + "order by " + COLUMN_NAME_ID + " LIMIT ? OFFSET ?"; try { - return DAO.getInstance().getJdbcTemp().query(templateSelectWhereIdOrderBy, new Object[]{limit, offset}, new PointEventDetectorRowMapper2()); + return DAO.getInstance().getJdbcTemp().query(templateSelectWhereIdOrderBy, new Object[]{limit, offset}, new PointEventDetectorDataPointIdRowMapper()); } catch (EmptyResultDataAccessException ex) { return Collections.emptyList(); } catch (IncorrectResultSizeDataAccessException ex) { From 4f9a04efb728bb96aa7fa1dd034b9535ea756f60 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 31 Aug 2023 10:31:45 +0200 Subject: [PATCH 09/43] #2675 Fixed longer time startup application - change level logging error to info --- .../dao/cache/PointEventDetectorCache.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/org/scada_lts/dao/cache/PointEventDetectorCache.java b/src/org/scada_lts/dao/cache/PointEventDetectorCache.java index 803095333a..28fbeebb5d 100644 --- a/src/org/scada_lts/dao/cache/PointEventDetectorCache.java +++ b/src/org/scada_lts/dao/cache/PointEventDetectorCache.java @@ -20,55 +20,55 @@ public PointEventDetectorCache(PointEventDetectorDAO pointEventDetectorDAO) { @Override public int insert(int dataPointId, PointEventDetectorVO pointEventDetector) { - LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); + LOG.info("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); return pointEventDetectorDAO.insert(dataPointId, pointEventDetector); } @Override public void update(int dataPointId, PointEventDetectorVO pointEventDetector) { - LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); + LOG.info("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); pointEventDetectorDAO.update(dataPointId, pointEventDetector); } @Override public void updateWithType(int dataPointId, PointEventDetectorVO pointEventDetector) { - LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); + LOG.info("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); pointEventDetectorDAO.updateWithType(dataPointId, pointEventDetector); } @Override public List selectPointEventDetectors(DataPointVO dataPoint) { - LOG.error("no cache: " + LoggingUtils.dataPointInfo(dataPoint)); + LOG.info("no cache: " + LoggingUtils.dataPointInfo(dataPoint)); return pointEventDetectorDAO.getPointEventDetectors(dataPoint); } @Override public void delete(int dataPointId, PointEventDetectorVO pointEventDetector) { - LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); + LOG.info("no cache: dataPointId: " + dataPointId + ", pointEventDetector: " + LoggingUtils.pointEventDetectorInfo(pointEventDetector)); pointEventDetectorDAO.delete(dataPointId, pointEventDetector); } @Override public void deleteWithId(String dataPointIds) { - LOG.error("no cache: dataPointIds: " + dataPointIds); + LOG.info("no cache: dataPointIds: " + dataPointIds); pointEventDetectorDAO.deleteWithId(dataPointIds); } @Override public PointEventDetectorVO selectPointEventDetector(int pointEventDetectorId) { - LOG.error("no cache: pointEventDetectorId: " + pointEventDetectorId); + LOG.info("no cache: pointEventDetectorId: " + pointEventDetectorId); return pointEventDetectorDAO.getPointEventDetector(pointEventDetectorId); } @Override public PointEventDetectorVO selectPointEventDetector(String pointEventDetectorXid, int dataPointId) { - LOG.error("no cache: dataPointId: " + dataPointId + ", pointEventDetectorXid: " + pointEventDetectorXid); + LOG.info("no cache: dataPointId: " + dataPointId + ", pointEventDetectorXid: " + pointEventDetectorXid); return pointEventDetectorDAO.getPointEventDetector(pointEventDetectorXid, dataPointId); } @Override public int selectDataPointIdByEventDetectorId(int pointEventDetectorId) { - LOG.error("no cache: pointEventDetectorId: " + pointEventDetectorId); + LOG.info("no cache: pointEventDetectorId: " + pointEventDetectorId); return pointEventDetectorDAO.getDataPointId(pointEventDetectorId); } } From abfb085843386bbc4e7dc846cf52438dac1a0f08 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Mon, 4 Sep 2023 09:13:24 +0200 Subject: [PATCH 10/43] #2684 error in the report when writing message in the alarm - corrected column name from COLUMN_NAME_COMMENT_TYPE on COLUMN_NAME_TYPE_KEY --- src/org/scada_lts/mango/service/ReportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/scada_lts/mango/service/ReportService.java b/src/org/scada_lts/mango/service/ReportService.java index d22e07069b..d6f2a4f521 100644 --- a/src/org/scada_lts/mango/service/ReportService.java +++ b/src/org/scada_lts/mango/service/ReportService.java @@ -402,7 +402,7 @@ public void processRow(ResultSet rs) throws SQLException { userComment.setComment(rs.getString(ReportInstanceUserCommentDAO.COLUMN_NAME_COMMENT_TEXT)); // Find the event and add the comment - int eventId = rs.getInt(ReportInstanceUserCommentDAO.COLUMN_NAME_COMMENT_TYPE); + int eventId = rs.getInt(ReportInstanceUserCommentDAO.COLUMN_NAME_TYPE_KEY); for (EventInstance event : events) { if (event.getId() == eventId) { if (event.getEventComments() == null) { From f16b92583435ed26cea88b0e2b465b2152b3da2e Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Mon, 4 Sep 2023 16:38:04 +0200 Subject: [PATCH 11/43] removed duplicate patterns: /userCommentExport/**, /dwr/call/plaincall/DataPointDetailsDwr.*.dwr in spring-security.xml --- WebContent/WEB-INF/spring-security.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/WebContent/WEB-INF/spring-security.xml b/WebContent/WEB-INF/spring-security.xml index 9187fc6878..9d2b83878d 100644 --- a/WebContent/WEB-INF/spring-security.xml +++ b/WebContent/WEB-INF/spring-security.xml @@ -190,7 +190,6 @@ - @@ -225,7 +224,6 @@ - From 5cc1fcfdbb40d7506ef130b71dc850fe815884b8 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 5 Sep 2023 12:40:09 +0200 Subject: [PATCH 12/43] #2686 Fixed SandboxWrapFactory for executing scripts by Rhino - set true flag javaPrimitiveWrap for SandboxWrapFactory; added tests in ScriptExecutorTest; refactor ScriptExecutorTest; Update env.properties for tests; Corrected SystemSettingsUtilsTest.when_getDataPointSynchronizedMode_then_low; --- .../scripting/SandboxWrapFactory.java | 5 + .../dataSource/meta/ScriptExecutorTest.java | 319 ++++++++++++++++-- test/env.properties | 48 ++- .../utils/SystemSettingsUtilsTest.java | 4 +- 4 files changed, 345 insertions(+), 31 deletions(-) diff --git a/src/org/scada_lts/scripting/SandboxWrapFactory.java b/src/org/scada_lts/scripting/SandboxWrapFactory.java index 8742093f65..2e6adec626 100644 --- a/src/org/scada_lts/scripting/SandboxWrapFactory.java +++ b/src/org/scada_lts/scripting/SandboxWrapFactory.java @@ -29,6 +29,11 @@ * @author Zuzana Maczek, grzegorz bylica Abil'I.T. development team, sdt@abilit.eu */ public class SandboxWrapFactory extends WrapFactory { + + public SandboxWrapFactory() { + super.setJavaPrimitiveWrap(false); + } + @Override public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, @SuppressWarnings("rawtypes") Class staticType) { return new SandboxNativeJavaObject(scope, javaObject, staticType); diff --git a/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java b/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java index 1d232f3254..ee784185c1 100644 --- a/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java +++ b/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java @@ -4,8 +4,12 @@ import br.org.scadabr.rt.scripting.context.DPCommandsScriptContextObject; import br.org.scadabr.rt.scripting.context.ScriptContextObject; import com.serotonin.mango.Common; +import com.serotonin.mango.DataTypes; import com.serotonin.mango.rt.RuntimeManager; +import org.junit.*; +import org.mozilla.javascript.ContextFactory; import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.scada_lts.scripting.SandboxContextFactory; import org.scada_lts.web.beans.ApplicationBeans; import utils.ScriptTestUtils; import com.serotonin.mango.rt.dataImage.DataPointRT; @@ -15,9 +19,6 @@ import com.serotonin.mango.rt.dataImage.types.*; import com.serotonin.mango.vo.User; import com.serotonin.mango.vo.permission.Permissions; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -41,29 +42,35 @@ "javax.activation.*", "javax.management.*"}) public class ScriptExecutorTest { + private static final double DELTA = .01; + + @BeforeClass + public static void initGlobal() { + ContextFactory.initGlobal(new SandboxContextFactory()); + } + @Before public void config() throws Exception { RuntimeManager runtimeManager = mock(RuntimeManager.class); DPCommandsScriptContextObject scriptContextObject = mock(DPCommandsScriptContextObject.class); ScriptTestUtils.configMock(runtimeManager, scriptContextObject); - } @Test public void test_execute_js_with_return_state_double() throws ScriptException, ResultTypeException { ScriptExecutor scriptExecutor = new ScriptExecutor(); PointValueTime pointValueTime = scriptExecutor.execute("return 2.2;", Collections.emptyMap(), - 0, 3, 0); + 0, DataTypes.NUMERIC, 0); double result = pointValueTime.getDoubleValue(); - Assert.assertEquals(2.2, result, 0.1); + Assert.assertEquals(2.2, result, DELTA); } @Test public void test_execute_js_with_return_state_int() throws ScriptException, ResultTypeException { ScriptExecutor scriptExecutor = new ScriptExecutor(); PointValueTime pointValueTime = scriptExecutor.execute("return 2;", Collections.emptyMap(), - 0, 2, 0); + 0, DataTypes.MULTISTATE, 0); int result = pointValueTime.getIntegerValue(); Assert.assertEquals(2, result); } @@ -72,7 +79,7 @@ public void test_execute_js_with_return_state_int() throws ScriptException, Resu public void test_execute_js_with_return_state_string() throws ScriptException, ResultTypeException { ScriptExecutor scriptExecutor = new ScriptExecutor(); PointValueTime pointValueTime = scriptExecutor.execute("return 'abc';", Collections.emptyMap(), - 0, 4, 0); + 0, DataTypes.ALPHANUMERIC, 0); String result = pointValueTime.getStringValue(); Assert.assertEquals("abc", result); } @@ -81,7 +88,7 @@ public void test_execute_js_with_return_state_string() throws ScriptException, R public void test_execute_js_with_return_state_boolean() throws ScriptException, ResultTypeException { ScriptExecutor scriptExecutor = new ScriptExecutor(); PointValueTime pointValueTime = scriptExecutor.execute("return true;", Collections.emptyMap(), - 0, 1, 0); + 0, DataTypes.BINARY, 0); boolean result = pointValueTime.getBooleanValue(); Assert.assertEquals(true, result); } @@ -99,7 +106,7 @@ public void test_execute_js_with_point_context_binary() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "if (p1.value) return 5; " + - "return 3;", context, 0, 2, 0); + "return 3;", context, 0, DataTypes.MULTISTATE, 0); //then: int result = pointValueTime.getIntegerValue(); @@ -115,7 +122,7 @@ public void test_execute_js_with_broken_point_context_binary() throws Exception //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "if (p1.value) return 5; " + - "return 3;", Collections.emptyMap(), 0, 2, 0); + "return 3;", Collections.emptyMap(), 0, DataTypes.MULTISTATE, 0); } @@ -133,7 +140,7 @@ public void test_execute_js_with_point_context_numeric() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "if (p1.value == 1.1) return 5; " + - "return 3;", context, 0, 3, 0); + "return 3;", context, 0, DataTypes.NUMERIC, 0); //then: int result = pointValueTime.getIntegerValue(); @@ -153,7 +160,7 @@ public void test_execute_js_with_point_context_numeric_toFixed() throws Exceptio //when: PointValueTime pointValueTime = scriptExecutor.execute( - "return p1.value.toFixed(2);", context, 0, 4, 0); + "return p1.value.toFixed(2);", context, 0, DataTypes.ALPHANUMERIC, 0); //then: String result = pointValueTime.getStringValue(); @@ -173,7 +180,7 @@ public void test_execute_js_with_point_context_alphanumeric() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "if (p1.value == 'alphanumeric test') return 5; " + - "return 3;", context, 0, 3, 0); + "return 3;", context, 0, DataTypes.NUMERIC, 0); //then: int result = pointValueTime.getIntegerValue(); @@ -195,7 +202,7 @@ public void test_execute_js_with_point_context_multistatic() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "if (p1.value == 1234) return 5; " + - "return 3;", context, 0, 3, 0); + "return 3;", context, 0, DataTypes.NUMERIC, 0); //then: int result = pointValueTime.getIntegerValue(); @@ -212,11 +219,11 @@ public void test_execute_js_with_java_PointValueTime() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "var pointValueTime = com.serotonin.mango.rt.dataImage.PointValueTime(new com.serotonin.mango.rt.dataImage.types.NumericValue(12345.5),0);" + - "return pointValueTime.getDoubleValue();", Collections.emptyMap(), 0, 3, 0); + "return pointValueTime.getDoubleValue();", Collections.emptyMap(), 0, DataTypes.NUMERIC, 0); //then: double result = pointValueTime.getDoubleValue(); - Assert.assertEquals(12345.5, result, 0.01); + Assert.assertEquals(12345.5, result, DELTA); } @@ -229,11 +236,11 @@ public void test_execute_js_with_java_PointValueTime_new() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "var pointValueTime = new com.serotonin.mango.rt.dataImage.PointValueTime(new com.serotonin.mango.rt.dataImage.types.NumericValue(12345.5),0);" + - "return pointValueTime.getDoubleValue();", Collections.emptyMap(), 0, 3, 0); + "return pointValueTime.getDoubleValue();", Collections.emptyMap(), 0, DataTypes.NUMERIC, 0); //then: double result = pointValueTime.getDoubleValue(); - Assert.assertEquals(12345.5, result, 0.01); + Assert.assertEquals(12345.5, result, DELTA); } @@ -246,7 +253,7 @@ public void test_execute_js_with_java_create_ViewDwr() throws Exception { //when: PointValueTime pointValueTime = scriptExecutor.execute("" + "var mydwr=new com.serotonin.mango.web.dwr.ViewDwr();" + - "return 'cde';", Collections.emptyMap(), 0, 4, 0); + "return 'cde';", Collections.emptyMap(), 0, DataTypes.ALPHANUMERIC, 0); //then: String result = pointValueTime.getStringValue(); @@ -269,10 +276,280 @@ public void test_execute_js_with_java_invoke_method_getLoggedUser_in_ViewDwr() t PointValueTime pointValueTime = scriptExecutor.execute("" + "var mydwr=new com.serotonin.mango.web.dwr.ViewDwr();" + "var user=mydwr.getLoggedUser();" + - "return user + '';", Collections.emptyMap(), 0, 4, 0); + "return user + '';", Collections.emptyMap(), 0, DataTypes.ALPHANUMERIC, 0); //then: String result = pointValueTime.getStringValue(); Assert.assertEquals(userName, result); } + + @Test + public void test_execute_js_with_point_context_alphanumeric_return_value() throws Exception { + + //given: + String value = "alphanumeric test"; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new AlphanumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value;", context, 0, DataTypes.ALPHANUMERIC, 0); + + //then: + String result = pointValueTime.getStringValue(); + Assert.assertEquals(value, result); + } + + @Test + public void test_execute_js_with_point_context_alphanumeric_return_value_plus_value() throws Exception { + + //given: + String value = "alphanumeric test"; + String expected = value + value; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new AlphanumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value + p1.value;", context, 0, DataTypes.ALPHANUMERIC, 0); + + //then: + String result = pointValueTime.getStringValue(); + Assert.assertEquals(expected, result); + } + + @Test + public void test_execute_js_with_point_context_alphanumeric_return_value_plus_abc() throws Exception { + + //given: + String value = "alphanumeric test"; + String diff = "_abc"; + String expected = "alphanumeric test" + diff; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new AlphanumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value + '" + diff + "';", context, 0, DataTypes.ALPHANUMERIC, 0); + + //then: + String result = pointValueTime.getStringValue(); + Assert.assertEquals(expected, result); + } + + @Test + public void test_execute_js_with_point_context_multistate_return_value() throws Exception { + + //given: + int value = 12; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new MultistateValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value;", context, 0, DataTypes.MULTISTATE, 0); + + //then: + int result = pointValueTime.getIntegerValue(); + Assert.assertEquals(value, result); + } + + @Test + public void test_execute_js_with_point_context_multistate_return_value_plus_value() throws Exception { + + //given: + int value = 12; + int expected = value + value; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new MultistateValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value + p1.value;", context, 0, DataTypes.MULTISTATE, 0); + + //then: + int result = pointValueTime.getIntegerValue(); + Assert.assertEquals(expected, result); + } + + @Test + public void test_execute_js_with_point_context_multistate_return_value_plus_4() throws Exception { + + //given: + int value = 12; + int diff = 4; + int expected = value + diff; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new MultistateValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value + " + diff + ";", context, 0, DataTypes.MULTISTATE, 0); + + //then: + int result = pointValueTime.getIntegerValue(); + Assert.assertEquals(expected, result); + } + + @Test + public void test_execute_js_with_point_context_numeric_return_value() throws Exception { + + //given: + double value = 12.12; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new NumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value;", context, 0, DataTypes.NUMERIC, 0); + + //then: + double result = pointValueTime.getDoubleValue(); + Assert.assertEquals(value, result, DELTA); + } + + @Test + public void test_execute_js_with_point_context_numeric_return_value_plus_value() throws Exception { + + //given: + double value = 12.12; + double expected = value + value; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new NumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value + p1.value;", context, 0, DataTypes.NUMERIC, 0); + + //then: + double result = pointValueTime.getDoubleValue(); + Assert.assertEquals(expected, result, DELTA); + } + + @Test + public void test_execute_js_with_point_context_numeric_return_value_plus_10_1() throws Exception { + + //given: + double value = 12.12; + double diff = 10.1; + double expected = value + diff; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new NumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value + " + diff + ";", context, 0, DataTypes.NUMERIC, 0); + + //then: + double result = pointValueTime.getDoubleValue(); + Assert.assertEquals(expected, result, DELTA); + } + + @Test + public void test_execute_js_with_point_context_binary_return_value() throws Exception { + + //given: + boolean value = true; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new BinaryValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value;", context, 0, DataTypes.BINARY, 0); + + //then: + boolean result = pointValueTime.getBooleanValue(); + Assert.assertEquals(value, result); + } + + @Test + public void test_execute_js_with_point_context_binary_return_value_or_value() throws Exception { + + //given: + boolean value = true; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new BinaryValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value || p1.value;", context, 0, DataTypes.BINARY, 0); + + //then: + boolean result = pointValueTime.getBooleanValue(); + Assert.assertEquals(value, result); + } + + @Test + public void test_execute_js_with_point_context_binary_return_value_or_false() throws Exception { + + //given: + boolean value = true; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new BinaryValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value || false;", context, 0, DataTypes.BINARY, 0); + + //then: + boolean result = pointValueTime.getBooleanValue(); + Assert.assertEquals(value, result); + } + + @Test + public void test_execute_js_with_point_context_binary_return_value_and_false() throws Exception { + + //given: + boolean value = true; + boolean expected = value && false; + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new BinaryValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value && false;", context, 0, DataTypes.BINARY, 0); + + //then: + boolean result = pointValueTime.getBooleanValue(); + Assert.assertEquals(expected, result); + } } diff --git a/test/env.properties b/test/env.properties index 1bedea2610..70ca032a42 100644 --- a/test/env.properties +++ b/test/env.properties @@ -22,8 +22,6 @@ # ALTERE A LINHA db.password=admin (COLOQUE A SENHA DEFINIDA POR VOC\u00ca) # N\u00c3O REMOVA A LINHA db.url.public=jdbc:postgresql://localhost:5432/postgres -#testowy!!!! - #db.type=postgres #db.url=jdbc:postgresql://localhost:5432/scadabr #db.url.public=jdbc:postgresql://localhost:5432/postgres @@ -34,7 +32,7 @@ db.type=mysql db.datasource=true -db.datasourceName=java:comp/test_env/jdbc/scadalts +db.datasourceName=java:comp/env/jdbc/scadalts #db.url=jdbc:mysql://localhost:3306/scadalts #db.username=root #db.password=root @@ -59,12 +57,12 @@ abilit.disableDataSourcesOnServerStart=false abilit.api.replace.alert.onview=true -abilit.cacheEnable=true +abilit.cacheEnable=false abilit.START_UPDATE_UNSILENCED_ALARM_LEVEL=100000 abilit.START_UPDATE_EVENT_DETECTORS=100000 abilit.START_UPDATE_PENDING_EVENTS=100000 abilit.MILLIS_SECONDS_PERIOD_UPDATE_UNSILENCED_ALARM_LEVEL=1000 -abilit.MILLIS_SECONDS_PERIOD_UPDATE_EVENT_DETECTORS=1000 +#abilit.MILLIS_SECONDS_PERIOD_UPDATE_EVENT_DETECTORS=1000 abilit.MILLIS_SECONDS_PERIOD_UPDATE_PENDING_EVENTS=1000 abilit.CRONE_UPDATE_CACHE_POINT_HIERARCHY=0 0/10 * * * ? @@ -86,17 +84,51 @@ abilit.HTTP_RETRIVER_DO_NOT_ALLOW_ENABLE_REACTIVATION=false #security.hashAlgorithm=NONE #grove.url=http://mango.serotoninsoftware.com/servlet +api.amcharts.aggregation.enabled=true +api.amcharts.aggregation.valuesLimit=20000 +api.amcharts.aggregation.limitFactor=1.5 + +user.cache.enabled=true +usersprofile.cache.enabled=true +permissions.cache.enabled=true +shareuser.cache.enabled=true +usercomment.cache.enabled=true +view.cache.enabled=true + +alarmlevel.highest.cache.enabled=true +alarmlevel.highest.cache.reset.enabled=true +alarmlevel.highest.cache.reset.cron=0 0 1/3 ? * * * + svg.validator.enabled=true svg.validator.only-xml=false svg.validator.schemas=svg/svg11-flat-20110816/svg11-flat-20110816.xsd;svg/svg10-flat-20010904/svg10-flat-20010904.xsd # Incompatible with SVG 1.0/1.1/1.1 Second Edition/1.1 Tiny/1.1 Basic - often found in files; # rdf:RDF - https://www.w3.org/TR/SVGTiny12/metadata.html svg.validator.tags.ignore=rdf:RDF;sodipodi:namedview;inkscape:perspective -svg.validator.messages.ignore=must appear on element;Attribute 'blend' is not allowed to appear in element 'feBlend'.;sodipodi:;inkscape:; +svg.validator.messages.ignore=must appear on element;Attribute 'blend' is not allowed to appear in element 'feBlend'.;sodipodi:;inkscape:;Attribute 'vector-effect' is not allowed to appear in element 'g'. +systemsettings.http.response.headers={} -datapoint.runtime.value.synchronized=partial +datapoint.runtime.value.synchronized=NONE comm.serial.timeout.read=500 comm.serial.timeout.write=500 comm.serial.timeout.mode=0 -comm.serial.sleep=0 \ No newline at end of file +comm.serial.sleep=1000 + +systemsettings.email.timeout=60000 +processing.workitems.limit=51 +processing.workitems.failed.limit=51 +processing.workitems.running.limit=51 +processing.workitems.running.repeat=51 +processing.workitems.history.longer.thanMs=1500 +processing.workitems.history.longer.limit=200 + +scadalts.security.js.access.denied.method.regexes=getDeclaredField;getPassword;getClass;getRuntime;exec +scadalts.security.js.access.denied.class.regexes=java.lang.Runtime;java.lang.Runtime.*;java.lang.reflect.*;java.lang.ref.*;java.lang.System.*;java.lang.System;java.lang.ClassLoader;java.lang.Compiler;java.lang.SecurityManager;java.lang.Thread;java.lang.ThreadGroup;java.lang.ThreadLocal +scadalts.security.js.access.granted.method.regexes=^.* +scadalts.security.js.access.granted.class.regexes=^.* + +view.forceFullScreen=false +view.hideShortcutDisableFullScreen=false +eventdetector.cache.enabled=true +event.pending.limit=101 \ No newline at end of file diff --git a/test/org/scada_lts/utils/SystemSettingsUtilsTest.java b/test/org/scada_lts/utils/SystemSettingsUtilsTest.java index dfe3e2a059..1fd60b89f8 100644 --- a/test/org/scada_lts/utils/SystemSettingsUtilsTest.java +++ b/test/org/scada_lts/utils/SystemSettingsUtilsTest.java @@ -24,12 +24,12 @@ public void config() { } @Test - public void when_getDataPointSynchronizedMode_then_medium() { + public void when_getDataPointSynchronizedMode_then_low() { //when: DataPointSyncMode result = SystemSettingsUtils.getDataPointSynchronizedMode(); //then: - assertEquals(DataPointSyncMode.MEDIUM, result); + assertEquals(DataPointSyncMode.LOW, result); } } \ No newline at end of file From 0750f0805b51f3c2a691498591646e679c5438a2 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 5 Sep 2023 12:48:06 +0200 Subject: [PATCH 13/43] #2686 Fixed SandboxWrapFactory for executing scripts by Rhino - corrected env.properties for tests; --- test/env.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/env.properties b/test/env.properties index 70ca032a42..93f858720d 100644 --- a/test/env.properties +++ b/test/env.properties @@ -32,7 +32,7 @@ db.type=mysql db.datasource=true -db.datasourceName=java:comp/env/jdbc/scadalts +db.datasourceName=java:comp/test_env/jdbc/scadalts #db.url=jdbc:mysql://localhost:3306/scadalts #db.username=root #db.password=root From 759bdad74d5344aa8d39fedb9d83fab5436cb62f Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 5 Sep 2023 14:58:54 +0200 Subject: [PATCH 14/43] added org.scada_lts.web.mvc.api to config in log4j2.xml --- webapp-resources/log4j2.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webapp-resources/log4j2.xml b/webapp-resources/log4j2.xml index 95db996d81..889af199c5 100644 --- a/webapp-resources/log4j2.xml +++ b/webapp-resources/log4j2.xml @@ -197,6 +197,9 @@ + + + From b8dd28030933c168dc623d691c4f3fdeb33aa50f Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 5 Sep 2023 16:29:29 +0200 Subject: [PATCH 15/43] #2686 Fixed SandboxWrapFactory for executing scripts by Rhino - revert SandboxWrapFactory.java (javaPrimitiveWrap = true); handle NativeJavaObject and getDefaultValue String in ScriptExecutor; added test ScriptExecutorTest.test_execute_js_with_point_context_alphanumeric_return_value_length; --- .../rt/dataSource/meta/ScriptExecutor.java | 6 +++++- .../scripting/SandboxWrapFactory.java | 4 ---- .../dataSource/meta/ScriptExecutorTest.java | 21 +++++++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java b/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java index 99f4ef9a4a..7b122fa067 100644 --- a/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java +++ b/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java @@ -31,6 +31,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.Scriptable; import com.serotonin.ShouldNeverHappenException; @@ -166,7 +167,10 @@ public PointValueTime execute(String script, if (o == null) result = null; else { - if (!(o instanceof AbstractPointWrapper)) { + if (o instanceof NativeJavaObject) { + NativeJavaObject nativeJavaObject = (NativeJavaObject)o; + result = nativeJavaObject.getDefaultValue(String.class); + } else if (!(o instanceof AbstractPointWrapper)) { result = o.toString(); } else { result = o; diff --git a/src/org/scada_lts/scripting/SandboxWrapFactory.java b/src/org/scada_lts/scripting/SandboxWrapFactory.java index 2e6adec626..912284a01b 100644 --- a/src/org/scada_lts/scripting/SandboxWrapFactory.java +++ b/src/org/scada_lts/scripting/SandboxWrapFactory.java @@ -30,10 +30,6 @@ */ public class SandboxWrapFactory extends WrapFactory { - public SandboxWrapFactory() { - super.setJavaPrimitiveWrap(false); - } - @Override public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, @SuppressWarnings("rawtypes") Class staticType) { return new SandboxNativeJavaObject(scope, javaObject, staticType); diff --git a/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java b/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java index ee784185c1..1648342aa9 100644 --- a/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java +++ b/test/com/serotonin/mango/rt/dataSource/meta/ScriptExecutorTest.java @@ -552,4 +552,25 @@ public void test_execute_js_with_point_context_binary_return_value_and_false() t boolean result = pointValueTime.getBooleanValue(); Assert.assertEquals(expected, result); } + + @Test + public void test_execute_js_with_point_context_alphanumeric_return_value_length() throws Exception { + + //given: + String value = "abc"; + String expected = String.valueOf(value.length()); + DataPointRT p1 = ScriptTestUtils.createDataPointRT(0, new AlphanumericValue(value)); + Map context = new HashMap<>(); + context.put("p1", p1); + + ScriptExecutor scriptExecutor = new ScriptExecutor(); + + //when: + PointValueTime pointValueTime = scriptExecutor.execute( + "return p1.value.length();", context, 0, DataTypes.ALPHANUMERIC, 0); + + //then: + String result = pointValueTime.getStringValue(); + Assert.assertEquals(expected, result); + } } From f72f2f6a749ae65a2a8bf6777058b76e52f98729 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 5 Sep 2023 16:46:07 +0200 Subject: [PATCH 16/43] #2686 Fixed SandboxWrapFactory for executing scripts by Rhino - revert ScriptExecutor; override toString for SandboxNativeJavaObject --- .../serotonin/mango/rt/dataSource/meta/ScriptExecutor.java | 5 +---- src/org/scada_lts/scripting/SandboxNativeJavaObject.java | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java b/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java index 7b122fa067..8dc5370f8a 100644 --- a/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java +++ b/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java @@ -167,10 +167,7 @@ public PointValueTime execute(String script, if (o == null) result = null; else { - if (o instanceof NativeJavaObject) { - NativeJavaObject nativeJavaObject = (NativeJavaObject)o; - result = nativeJavaObject.getDefaultValue(String.class); - } else if (!(o instanceof AbstractPointWrapper)) { + if (!(o instanceof AbstractPointWrapper)) { result = o.toString(); } else { result = o; diff --git a/src/org/scada_lts/scripting/SandboxNativeJavaObject.java b/src/org/scada_lts/scripting/SandboxNativeJavaObject.java index a1cc925760..b7af25ff2d 100644 --- a/src/org/scada_lts/scripting/SandboxNativeJavaObject.java +++ b/src/org/scada_lts/scripting/SandboxNativeJavaObject.java @@ -71,4 +71,8 @@ public Object get(String methodName, Scriptable start) { return NOT_FOUND; } + @Override + public String toString() { + return String.valueOf(super.getDefaultValue(String.class)); + } } From e1b19629a217a6bdef86003c21a1a072a540ecd0 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 5 Sep 2023 16:47:25 +0200 Subject: [PATCH 17/43] #2686 Fixed SandboxWrapFactory for executing scripts by Rhino - removed unuse import ScriptExecutor --- src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java b/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java index 8dc5370f8a..99f4ef9a4a 100644 --- a/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java +++ b/src/com/serotonin/mango/rt/dataSource/meta/ScriptExecutor.java @@ -31,7 +31,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mozilla.javascript.Context; -import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.Scriptable; import com.serotonin.ShouldNeverHappenException; From 01c536918dec1a25b94335ea12fb6fbbbb4147e3 Mon Sep 17 00:00:00 2001 From: Kamil Jarmusik Date: Mon, 11 Sep 2023 21:03:54 +0200 Subject: [PATCH 18/43] #2694 Work-items API extension - - Adding the threadName parameter to the data returned by the API, specifying the name of the thread that executed a given work item; - Naming threads so that you can detect which threads come from the pools: high, medium, low priority; - Adding the name of the work item that is currently executing to the thread to recognize which work items are executing for a longer time, and to recognize which thread; - Refactoring the work-items and threads API to make it more logical and easier to use; - Fixed method in ScheduledWorkItemInfoApiService: getThreadClassesForState, getThreadNamesForState for empty result; - Added parameter thread.name.additional.length to env.properties 0 - without additional name to thread --- doc/ThreadInfoAPI.yaml | 104 ++++------- doc/WorkItemInfoAPI.yaml | 61 ++++--- .../persistent/PersistentDataSourceRT.java | 20 +- .../work/AbstractBeforeAfterWorkItem.java | 42 +++-- .../mango/rt/maint/work/WorkItemMetrics.java | 4 +- .../persistent/PersistentSendThread.java | 5 +- .../mango/web/dwr/beans/ImportTask.java | 3 +- src/com/serotonin/timer/AbstractTimer.java | 5 +- .../sync/SingleExecutorSingleWaiter.java | 3 +- .../serotonin/timer/sync/Synchronizer.java | 5 +- .../scada_lts/mango/service/EventService.java | 5 +- src/org/scada_lts/utils/ThreadUtils.java | 21 +++ .../api/ScheduledWorkItemInfoApiService.java | 11 +- .../mvc/api/StateThreadInfoApiService.java | 18 +- .../scada_lts/web/mvc/api/ThreadInfoAPI.java | 171 ++++++++---------- .../web/mvc/api/ThreadInfoApiService.java | 49 +---- .../web/mvc/api/WorkItemInfoAPI.java | 104 ++++++----- .../web/mvc/api/WorkItemInfoApiService.java | 20 +- .../api/json/ScheduledWorkItemInfoList.java | 8 +- .../web/mvc/api/json/ThreadInfoList.java | 24 +++ webapp-resources/env.properties | 3 +- 21 files changed, 346 insertions(+), 340 deletions(-) create mode 100644 src/org/scada_lts/utils/ThreadUtils.java create mode 100644 src/org/scada_lts/web/mvc/api/json/ThreadInfoList.java diff --git a/doc/ThreadInfoAPI.yaml b/doc/ThreadInfoAPI.yaml index 2e436b1c97..30e54beaa3 100644 --- a/doc/ThreadInfoAPI.yaml +++ b/doc/ThreadInfoAPI.yaml @@ -41,108 +41,110 @@ paths: '200': description: 'Get successful' headers: { } - /threads/group-by/count/: + /threads/classes/: get: tags: - ThreadInfoAPI - description: 'Group threads count by thread' + description: 'Get all thread classes' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/thread-stack/: + /threads/group-by/classes/count/: get: tags: - ThreadInfoAPI - description: 'Group stacks by thread' + description: 'Group threads count by thread' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/thread-stack/classes/: + /threads/group-by/stack/: get: tags: - ThreadInfoAPI - description: 'Group stack classes by thread' + description: 'Group threads by stack' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/thread-stack/count/: + /threads/group-by/stack/classes/: get: tags: - ThreadInfoAPI - description: 'Group stacks count by thread' + description: 'Group threads by stack then classes' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/stack-thread/: + /threads/group-by/stack/count/: get: tags: - ThreadInfoAPI - description: 'Group threads by stack' + description: 'Group threads by stack then count' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/stack-thread/classes/: + /threads/group-by/stack/names/: get: tags: - ThreadInfoAPI - description: 'Group threads by stack then classes' + description: 'Group threads by stack then names' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/stack-thread/count/: + /threads/group-by/states/: get: tags: - - ThreadInfoAPI - description: 'Group threads by stack then count' + - StateThreadInfoAPI + description: 'Group threads by state' responses: '200': description: 'Get successful' headers: { } - /threads/group-by/stack-thread/names/: + /threads/group-by/states/classes/: get: tags: - - ThreadInfoAPI - description: 'Group threads by stack then names' + - StateThreadInfoAPI + description: 'Group thread classes by state' responses: '200': description: 'Get successful' headers: { } - /threads/states/: + /threads/group-by/states/count/: get: tags: - StateThreadInfoAPI - description: 'Get all states' + description: 'Group threads count by state' responses: '200': description: 'Get successful' headers: { } - /threads/states/state/{state}/: + /threads/group-by/states/names/: get: tags: - StateThreadInfoAPI - description: 'Get threads for state' + description: 'Group thread namew by state' responses: '200': description: 'Get successful' headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads/states/state/{state}/count/: + /threads/states/: get: tags: - StateThreadInfoAPI - description: 'Get threads count for state' + description: 'Get all states' + responses: + '200': + description: 'Get successful' + headers: { } + /threads/states/{state}/: + get: + tags: + - StateThreadInfoAPI + description: 'Get threads for state' responses: '200': description: 'Get successful' @@ -154,7 +156,7 @@ paths: schema: type: 'string' enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads/states/state/{state}/classes/: + /threads/states/{state}/classes/: get: tags: - StateThreadInfoAPI @@ -170,7 +172,7 @@ paths: schema: type: 'string' enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads/states/state/{state}/names/: + /threads/states/{state}/names/: get: tags: - StateThreadInfoAPI @@ -186,39 +188,3 @@ paths: schema: type: 'string' enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads/states/group-by/: - get: - tags: - - StateThreadInfoAPI - description: 'Group threads by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads/states/group-by/count/: - get: - tags: - - StateThreadInfoAPI - description: 'Group threads count by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads/states/group-by/classes/: - get: - tags: - - StateThreadInfoAPI - description: 'Group thread classes by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads/states/group-by/names/: - get: - tags: - - StateThreadInfoAPI - description: 'Group thread namew by state' - responses: - '200': - description: 'Get successful' - headers: { } \ No newline at end of file diff --git a/doc/WorkItemInfoAPI.yaml b/doc/WorkItemInfoAPI.yaml index 34cded218b..5e284e1734 100644 --- a/doc/WorkItemInfoAPI.yaml +++ b/doc/WorkItemInfoAPI.yaml @@ -23,7 +23,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/group-by/: + /work-items/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -32,7 +32,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/group-by/count/: + /work-items/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -50,7 +50,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/executed/group-by/: + /work-items/executed/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -59,7 +59,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/executed/group-by/count/: + /work-items/executed/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -77,7 +77,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/failed/group-by/: + /work-items/failed/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -86,7 +86,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/failed/group-by/count/: + /work-items/failed/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -104,7 +104,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/running/group-by/: + /work-items/running/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -113,7 +113,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/running/group-by/count/: + /work-items/running/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -131,7 +131,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/success/group-by/: + /work-items/success/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -140,7 +140,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/success/group-by/count/: + /work-items/success/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -158,7 +158,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/all/group-by/: + /work-items/all/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -167,7 +167,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/all/group-by/count/: + /work-items/all/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -191,7 +191,7 @@ paths: required: true schema: type: 'integer' - /work-items/longer/{executedMs}/group-by/: + /work-items/longer/{executedMs}/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -206,7 +206,7 @@ paths: required: true schema: type: 'integer' - /work-items/longer/{executedMs}/group-by/count/: + /work-items/longer/{executedMs}/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -221,7 +221,7 @@ paths: required: true schema: type: 'integer' - /work-items/longer/{executedMs}/history/: + /work-items/history/longer/{executedMs}/: get: tags: - WorkItemInfoAPI @@ -236,7 +236,7 @@ paths: required: true schema: type: 'integer' - /work-items/longer/{executedMs}/history/group-by/: + /work-items/history/longer/{executedMs}/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -251,7 +251,7 @@ paths: required: true schema: type: 'integer' - /work-items/longer/{executedMs}/history/group-by/count/: + /work-items/history/longer/{executedMs}/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -281,7 +281,7 @@ paths: required: true schema: type: 'integer' - /work-items/less/{executedMs}/group-by/: + /work-items/less/{executedMs}/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -296,7 +296,7 @@ paths: required: true schema: type: 'integer' - /work-items/less/{executedMs}/group-by/count/: + /work-items/less/{executedMs}/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -327,7 +327,7 @@ paths: schema: type: 'string' enum: [ HIGH, MEDIUM, LOW ] - /work-items/priority/{priority}/group-by/: + /work-items/priority/{priority}/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -343,7 +343,7 @@ paths: schema: type: 'string' enum: [ HIGH, MEDIUM, LOW ] - /work-items/priority/{priority}/group-by/count/: + /work-items/priority/{priority}/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -368,7 +368,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/scheduled/group-by/: + /work-items/scheduled/group-by-classes/: get: tags: - ScheduledWorkItemInfoAPI @@ -377,7 +377,7 @@ paths: '200': description: "Get successful" headers: { } - /work-items/scheduled/group-by/count/: + /work-items/scheduled/group-by-classes/count/: get: tags: - ScheduledWorkItemInfoAPI @@ -386,7 +386,16 @@ paths: '200': description: "Get successful" headers: { } - /work-items/scheduled/state/{state}/: + /work-items/scheduled/states/: + get: + tags: + - ScheduledWorkItemInfoAPI + description: 'Get active scheduled work items' + responses: + '200': + description: "Get successful" + headers: { } + /work-items/scheduled/states/{state}/: get: tags: - ScheduledWorkItemInfoAPI @@ -402,7 +411,7 @@ paths: schema: type: 'string' enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - /work-items/scheduled/state/{state}/group-by/: + /work-items/scheduled/states/{state}/group-by-classes/: get: tags: - ScheduledWorkItemInfoAPI @@ -418,7 +427,7 @@ paths: schema: type: 'string' enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - /work-items/scheduled/state/{state}/group-by/count/: + /work-items/scheduled/states/{state}/group-by-classes/count/: get: tags: - ScheduledWorkItemInfoAPI diff --git a/src/com/serotonin/mango/rt/dataSource/persistent/PersistentDataSourceRT.java b/src/com/serotonin/mango/rt/dataSource/persistent/PersistentDataSourceRT.java index 0da0a86247..f59ba1fd59 100644 --- a/src/com/serotonin/mango/rt/dataSource/persistent/PersistentDataSourceRT.java +++ b/src/com/serotonin/mango/rt/dataSource/persistent/PersistentDataSourceRT.java @@ -12,13 +12,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; +import com.serotonin.mango.util.LoggingUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.serotonin.db.IntValuePair; import com.serotonin.mango.Common; import com.serotonin.mango.db.dao.DataPointDao; -import com.serotonin.mango.db.dao.PointValueDao; import com.serotonin.mango.rt.dataImage.DataPointRT; import com.serotonin.mango.rt.dataImage.PointValueTime; import com.serotonin.mango.rt.dataImage.types.AlphanumericValue; @@ -165,9 +166,10 @@ public void run() { try { Socket socket = serverSocket.accept(); log.info("Received socket from " + socket.getRemoteSocketAddress()); - ConnectionHandler ch = new ConnectionHandler(socket); + String name = WorkItemPriority.HIGH + " - " + LoggingUtils.dataSourceInfo(this); + ConnectionHandler ch = new ConnectionHandler(socket, name); connectionHandlers.add(ch); - Common.timer.execute(ch); + Common.timer.execute(ch, name + " - " + ch.getClass().getName()); } catch (SocketTimeoutException e) { // no op @@ -186,12 +188,20 @@ class ConnectionHandler implements Runnable { int version = 3; private final ByteQueue writeBuffer = new ByteQueue(); private final List indexedXids = new ArrayList(); - final PointValueDao pointValueDao = new PointValueDao(); + final String details; private final long connectionTime; private long packetsReceived; + @Deprecated public ConnectionHandler(Socket socket) { this.socket = socket; + this.details = ""; + connectionTime = System.currentTimeMillis(); + } + + public ConnectionHandler(Socket socket, String details) { + this.socket = socket; + this.details = details; connectionTime = System.currentTimeMillis(); } @@ -373,7 +383,7 @@ private void runImpl() throws IOException, PersistentProtocolException, Persiste continue; if (packet.getType() == PacketType.RANGE_COUNT) { - Common.timer.execute(new RangeCountHandler(packet, out)); + Common.timer.execute(new RangeCountHandler(packet, out), details); continue; } diff --git a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java index 4e3315d5e9..9771d95448 100644 --- a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java +++ b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java @@ -2,7 +2,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.scada_lts.serorepl.utils.StringUtils; import org.scada_lts.utils.SystemSettingsUtils; +import org.scada_lts.utils.ThreadUtils; import java.util.HashMap; import java.util.Map; @@ -20,8 +22,10 @@ public abstract class AbstractBeforeAfterWorkItem implements WorkItem, BeforeWor private volatile boolean workFailed = false; private volatile boolean running = false; private volatile int executedMs = -1; - - private volatile String errorMessage = ""; + private volatile String threadName = ""; + private volatile String failedMessage = ""; + private volatile String workFailedMessage = ""; + private final String suffixThreadName = ThreadUtils.reduceName(" - " + WorkItemPriority.priorityOf(getPriority()) + " - " + this.getDetails()); public static WorkItems failedWorkItems() { return FAILED_WORK_ITEMS; @@ -50,23 +54,27 @@ private static void addWorkItemIfNotRunning(WorkItem workItem, boolean running) @Override public final void execute() { long startMs = System.currentTimeMillis(); + this.threadName = Thread.currentThread().getName(); + if(!StringUtils.isEmpty(this.suffixThreadName)) + Thread.currentThread().setName(this.threadName + this.suffixThreadName); boolean runningNow = this.running; this.running = true; this.executedMs = -1; this.workFailed = false; this.executed = false; this.success = false; - this.errorMessage = ""; + this.failedMessage = ""; + this.workFailedMessage = ""; addWorkItemIfNotRunning(this, runningNow); boolean failed = false; - boolean workFailed = false; + boolean workFailedTemp = false; String msg = ""; Map exceptions = new HashMap<>(); try { try { beforeWork(); } catch (Exception beforeWorkException) { - workFailed = true; + workFailedTemp = true; failed = true; msg = beforeWorkException.getMessage(); exceptions.put("beforeWork", beforeWorkException); @@ -81,7 +89,7 @@ public final void execute() { try { work(); } catch (Exception workException) { - workFailed = true; + workFailedTemp = true; failed = true; msg = workException.getMessage(); exceptions.put("work", workException); @@ -112,7 +120,7 @@ public final void execute() { workFinally(exceptions); } catch (Exception workFinallyException) { failed = true; - msg = workFinallyException.getMessage(); + this.failedMessage = workFinallyException.getMessage(); exceptions.put("workFinally", workFinallyException); try { workFinallyFail(workFinallyException, exceptions); @@ -122,12 +130,14 @@ public final void execute() { } } this.success = !failed; - this.workFailed = workFailed; + this.workFailed = workFailedTemp; this.executedMs = (int)(System.currentTimeMillis() - startMs); this.executed = true; - this.errorMessage = msg; + this.workFailedMessage = msg; addWorkItemAfterExecuted(this, failed, executedMs); this.running = false; + if(!StringUtils.isEmpty(this.suffixThreadName)) + Thread.currentThread().setName(this.threadName); } } @@ -185,7 +195,17 @@ public int getExecutedMs() { } @Override - public String getErrorMessage() { - return errorMessage; + public String getFailedMessage() { + return failedMessage; + } + + @Override + public String getWorkFailedMessage() { + return workFailedMessage; + } + + @Override + public String getThreadName() { + return threadName; } } diff --git a/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java b/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java index e18100225a..3884452048 100644 --- a/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java +++ b/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java @@ -6,5 +6,7 @@ public interface WorkItemMetrics { boolean isWorkFailed(); boolean isRunning(); int getExecutedMs(); - String getErrorMessage(); + String getFailedMessage(); + String getWorkFailedMessage(); + String getThreadName(); } diff --git a/src/com/serotonin/mango/rt/publish/persistent/PersistentSendThread.java b/src/com/serotonin/mango/rt/publish/persistent/PersistentSendThread.java index 6fcc4c10ef..7d2e65d8c2 100644 --- a/src/com/serotonin/mango/rt/publish/persistent/PersistentSendThread.java +++ b/src/com/serotonin/mango/rt/publish/persistent/PersistentSendThread.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -395,7 +396,7 @@ synchronized boolean startSync() { } syncHandler = new SyncHandler(this); - Common.timer.execute(syncHandler); + Common.timer.execute(syncHandler, WorkItemPriority.HIGH + " - name: " + this.getName() + ", id: " + this.getId() + " - " + this.getClass().getName() + ".startSync"); return true; } @@ -418,7 +419,7 @@ void writePointHierarchy() { public void run() { writePointHierarchy(new DataPointDao().getPointHierarchy()); } - }); + }, WorkItemPriority.HIGH + " - name: " + this.getName() + ", id: " + this.getId() + " - " + this.getClass().getName() + ".writePointHierarchy"); } synchronized void writePointHierarchy(PointHierarchy hierarchy) { diff --git a/src/com/serotonin/mango/web/dwr/beans/ImportTask.java b/src/com/serotonin/mango/web/dwr/beans/ImportTask.java index 6b6f86a30e..604c2f61a5 100644 --- a/src/com/serotonin/mango/web/dwr/beans/ImportTask.java +++ b/src/com/serotonin/mango/web/dwr/beans/ImportTask.java @@ -50,6 +50,7 @@ import com.serotonin.mango.rt.dataImage.PointValueTime; import com.serotonin.mango.rt.dataImage.types.MangoValue; import com.serotonin.mango.rt.event.type.EventType; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; import com.serotonin.mango.util.BackgroundContext; import com.serotonin.mango.util.LocalizableJsonException; import com.serotonin.mango.view.View; @@ -173,7 +174,7 @@ public ImportTask(JsonReader reader, JsonObject root, usersProfiles = nonNullList(root, EmportDwr.USERS_PROFILES); reports = nonNullList(root, EmportDwr.REPORTS); - Common.timer.execute(this); + Common.timer.execute(this, WorkItemPriority.HIGH + " - " + this.getClass().getName()); } diff --git a/src/com/serotonin/timer/AbstractTimer.java b/src/com/serotonin/timer/AbstractTimer.java index c2713d0b44..03ce21fa2c 100644 --- a/src/com/serotonin/timer/AbstractTimer.java +++ b/src/com/serotonin/timer/AbstractTimer.java @@ -1,6 +1,7 @@ package com.serotonin.timer; import org.apache.commons.lang3.StringUtils; +import org.scada_lts.utils.ThreadUtils; import java.util.List; @@ -15,7 +16,7 @@ public void execute(Runnable command, String name) { if (StringUtils.isBlank(name)) execute(command); else - execute(new NamedRunnable(command, name)); + execute(new NamedRunnable(command, ThreadUtils.reduceName(name))); } abstract public void execute(ScheduledRunnable command, long fireTime); @@ -24,7 +25,7 @@ public void execute(ScheduledRunnable command, long fireTime, String name) { if (StringUtils.isBlank(name)) execute(command, fireTime); else - execute(new ScheduledNamedRunnable(command, name), fireTime); + execute(new ScheduledNamedRunnable(command, ThreadUtils.reduceName(name)), fireTime); } final public TimerTask schedule(TimerTask task) { diff --git a/src/com/serotonin/timer/sync/SingleExecutorSingleWaiter.java b/src/com/serotonin/timer/sync/SingleExecutorSingleWaiter.java index a7e0729c28..99a8028556 100644 --- a/src/com/serotonin/timer/sync/SingleExecutorSingleWaiter.java +++ b/src/com/serotonin/timer/sync/SingleExecutorSingleWaiter.java @@ -4,6 +4,7 @@ */ package com.serotonin.timer.sync; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; import com.serotonin.timer.AbstractTimer; /** @@ -39,7 +40,7 @@ public void execute(Runnable task) { } void executeImpl() { - timer.execute(new TaskWrapper(executing)); + timer.execute(new TaskWrapper(executing), WorkItemPriority.HIGH + " - " + this.getClass().getName()); } class TaskWrapper implements Runnable { diff --git a/src/com/serotonin/timer/sync/Synchronizer.java b/src/com/serotonin/timer/sync/Synchronizer.java index f620b48f66..999e29009c 100644 --- a/src/com/serotonin/timer/sync/Synchronizer.java +++ b/src/com/serotonin/timer/sync/Synchronizer.java @@ -5,6 +5,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; import org.apache.commons.lang3.StringUtils; import com.serotonin.timer.AbstractTimer; @@ -54,7 +55,7 @@ public void executeAndWait(AbstractTimer timer) { if (maxConcurrent <= 0 || tasks.size() <= maxConcurrent) { // Start all of the tasks. for (TaskWrapper tw : tasks) - timer.execute(tw); + timer.execute(tw, WorkItemPriority.HIGH + " - " + this.getClass().getName()); running = tasks; } else { @@ -66,7 +67,7 @@ public void executeAndWait(AbstractTimer timer) { // Start tasks while (running.size() < maxConcurrent && !remaining.isEmpty()) { TaskWrapper tw = remaining.remove(remaining.size() - 1); - timer.execute(tw); + timer.execute(tw, WorkItemPriority.HIGH + " - " + this.getClass().getName()); running.add(tw); } diff --git a/src/org/scada_lts/mango/service/EventService.java b/src/org/scada_lts/mango/service/EventService.java index 4574814ee2..67491f83f5 100644 --- a/src/org/scada_lts/mango/service/EventService.java +++ b/src/org/scada_lts/mango/service/EventService.java @@ -24,6 +24,7 @@ import com.serotonin.mango.rt.event.type.AuditEventType; import com.serotonin.mango.rt.event.type.AuditEventUtils; import com.serotonin.mango.rt.event.type.EventType; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; import com.serotonin.mango.vo.User; import com.serotonin.mango.vo.UserComment; import com.serotonin.mango.vo.event.EventHandlerVO; @@ -32,7 +33,6 @@ import org.apache.commons.logging.LogFactory; import org.quartz.SchedulerException; import org.scada_lts.cache.PendingEventsCache; -import org.scada_lts.config.ScadaConfig; import org.scada_lts.dao.DAO; import org.scada_lts.dao.IUserCommentDAO; import org.scada_lts.dao.event.EventDAO; @@ -214,7 +214,8 @@ public List getPendingEventsForDataPoint(int dataPointId, int use userEvents = Collections.emptyList(); addToCache(userId, userEvents); //TODO rewrite to delete relation of seroUtils - Common.timer.execute(new UserPendingEventRetriever(userId)); + UserPendingEventRetriever userPendingEventRetriever = new UserPendingEventRetriever(userId); + Common.timer.execute(userPendingEventRetriever, WorkItemPriority.HIGH + " - dataPointId: " + dataPointId + ", userId: " + userId + " - " + userPendingEventRetriever.getClass().getName()); } List list = null; for (EventInstance e : userEvents) { diff --git a/src/org/scada_lts/utils/ThreadUtils.java b/src/org/scada_lts/utils/ThreadUtils.java new file mode 100644 index 0000000000..111991d9db --- /dev/null +++ b/src/org/scada_lts/utils/ThreadUtils.java @@ -0,0 +1,21 @@ +package org.scada_lts.utils; + +import org.scada_lts.config.ScadaConfig; + +import java.io.IOException; + +public final class ThreadUtils { + + private ThreadUtils() {} + + public static String reduceName(String name) { + try { + int limit = ScadaConfig.getInstance().getInt("thread.name.additional.length", 0); + if(limit == 0) + return ""; + return name.length() > limit ? name.substring(0, limit) + ".." : name; + } catch (IOException e) { + return ""; + } + } +} diff --git a/src/org/scada_lts/web/mvc/api/ScheduledWorkItemInfoApiService.java b/src/org/scada_lts/web/mvc/api/ScheduledWorkItemInfoApiService.java index 75f5ff7909..bfed3f65b3 100644 --- a/src/org/scada_lts/web/mvc/api/ScheduledWorkItemInfoApiService.java +++ b/src/org/scada_lts/web/mvc/api/ScheduledWorkItemInfoApiService.java @@ -30,6 +30,13 @@ public List getScheduledWorkItems(HttpServletRequest request, .collect(Collectors.toList()); } + public List getScheduledWorkItemsStates(HttpServletRequest request) { + checkIfNonAdminThenUnauthorized(request); + return Common.timer.getTasks().stream() + .map(a -> TimerTaskState.stateOf(a)) + .collect(Collectors.toList()); + } + public Map getScheduledWorkItemsGroupByClassNameCount(HttpServletRequest request) { checkIfNonAdminThenUnauthorized(request); return Common.timer.getTasks().stream() @@ -40,7 +47,7 @@ public Map> getScheduledWorkItemsGroupByClassNam checkIfNonAdminThenUnauthorized(request); return Common.timer.getTasks().stream() .map(ScheduledWorkItem::new) - .collect(Collectors.groupingBy(a -> a.getClass().getName(), Collectors.toList())); + .collect(Collectors.groupingBy(ScheduledWorkItem::getClassName, Collectors.toList())); } public Map getScheduledWorkItemsGroupByClassNameCount(HttpServletRequest request, TimerTaskState state) { @@ -55,6 +62,6 @@ public Map> getScheduledWorkItemsGroupByClassNam return Common.timer.getTasks().stream() .filter(a -> TimerTaskState.stateOf(a) == state) .map(ScheduledWorkItem::new) - .collect(Collectors.groupingBy(a -> a.getClass().getName(), Collectors.toList())); + .collect(Collectors.groupingBy(ScheduledWorkItem::getClassName, Collectors.toList())); } } diff --git a/src/org/scada_lts/web/mvc/api/StateThreadInfoApiService.java b/src/org/scada_lts/web/mvc/api/StateThreadInfoApiService.java index 2b709e80db..2de6452e56 100644 --- a/src/org/scada_lts/web/mvc/api/StateThreadInfoApiService.java +++ b/src/org/scada_lts/web/mvc/api/StateThreadInfoApiService.java @@ -44,24 +44,13 @@ public List getThreadsForState(Thread.State state, HttpServletReques } } - public Long getThreadsCountForState(Thread.State state, HttpServletRequest request) { - checkIfNonAdminThenUnauthorized(request); - try { - Map map = groupByAndSort(getThreadStack(), groupByStatesCounting(), - Map.Entry.comparingByValue(Comparator.reverseOrder())); - Long result = map.get(new Value(state.name())); - return result == null ? 0 : result; - } catch (Exception e) { - throw new InternalServerErrorException(e, request.getRequestURI()); - } - } - public List getThreadClassesForState(Thread.State state, HttpServletRequest request) { checkIfNonAdminThenUnauthorized(request); try { Map> map = groupByAndSort(getThreadStack(), groupByStatesClass(), Comparator.comparing(entry -> entry.getValue().size(), Comparator.reverseOrder())); - return map.get(new Value(state.name())); + List response = map.get(new Value(state.name())); + return response == null ? new ArrayList<>() : response; } catch (Exception e) { throw new InternalServerErrorException(e, request.getRequestURI()); } @@ -72,7 +61,8 @@ public List getThreadNamesForState(Thread.State state, HttpServletRequest try { Map> map = groupByAndSort(getThreadStack(), groupByStatesName(), Comparator.comparing(entry -> entry.getValue().size(), Comparator.reverseOrder())); - return map.get(new Value(state.name())); + List response = map.get(new Value(state.name())); + return response == null ? new ArrayList<>() : response; } catch (Exception e) { throw new InternalServerErrorException(e, request.getRequestURI()); } diff --git a/src/org/scada_lts/web/mvc/api/ThreadInfoAPI.java b/src/org/scada_lts/web/mvc/api/ThreadInfoAPI.java index ee7d670a48..aa87df4c14 100644 --- a/src/org/scada_lts/web/mvc/api/ThreadInfoAPI.java +++ b/src/org/scada_lts/web/mvc/api/ThreadInfoAPI.java @@ -1,6 +1,7 @@ package org.scada_lts.web.mvc.api; import org.scada_lts.web.mvc.api.json.ThreadInfo; +import org.scada_lts.web.mvc.api.json.ThreadInfoList; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -19,143 +20,115 @@ public class ThreadInfoAPI { private final ThreadInfoApiService threadInfoApiService; + private final StateThreadInfoApiService stateThreadInfoApiService; - public ThreadInfoAPI(ThreadInfoApiService threadInfoApiService) { + public ThreadInfoAPI(ThreadInfoApiService threadInfoApiService, StateThreadInfoApiService stateThreadInfoApiService) { this.threadInfoApiService = threadInfoApiService; + this.stateThreadInfoApiService = stateThreadInfoApiService; } - @GetMapping(value = "/") - public ResponseEntity> getThreads(HttpServletRequest request) { + @GetMapping(value = "") + public ResponseEntity> getThreads(HttpServletRequest request) { List response = threadInfoApiService.getThreads(request); - return new ResponseEntity<>(response, HttpStatus.OK); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); } - @GetMapping(value = "/stack/") - public ResponseEntity> getStackTraceElements(HttpServletRequest request) { + @GetMapping(value = "/stack") + public ResponseEntity> getStackTraceElements(HttpServletRequest request) { List response = threadInfoApiService.getStackTraceElements(request); - return new ResponseEntity<>(response, HttpStatus.OK); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); } - @GetMapping(value = "/names/") - public ResponseEntity> getThreadNames(HttpServletRequest request) { + @GetMapping(value = "/names") + public ResponseEntity> getThreadNames(HttpServletRequest request) { List response = threadInfoApiService.getThreadNames(request); - return new ResponseEntity<>(response, HttpStatus.OK); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); + } + + @GetMapping(value = "/classes") + public ResponseEntity> getThreadClasses(HttpServletRequest request) { + List response = threadInfoApiService.getThreadClasses(request); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); } - @GetMapping(value = "/group-by/count/") + @GetMapping(value = "/group-by/classes/count") public ResponseEntity> getThreadsGroupByClassCount(HttpServletRequest request) { Map response = threadInfoApiService.getThreadsGroupByClassCount(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/thread-stack/") - public ResponseEntity> getThreadsGroupByThreadStack(HttpServletRequest request) { - Map response = threadInfoApiService.getThreadsGroupByThreadStack(request); + @GetMapping(value = "/group-by/stack") + public ResponseEntity, List>> getThreadsGroupByStackThread(HttpServletRequest request) { + Map, List> response = threadInfoApiService.getThreadsGroupByStackThread(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/thread-stack/classes/") - public ResponseEntity> getThreadsGroupByThreadStackClasses(HttpServletRequest request) { - Map response = threadInfoApiService.getThreadsGroupByThreadStackClasses(request); + @GetMapping(value = "/group-by/stack/classes") + public ResponseEntity, List>> getThreadsGroupByStackThreadClasses(HttpServletRequest request) { + Map, List> response = threadInfoApiService.getThreadsGroupByStackThreadClasses(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/thread-stack/count/") - public ResponseEntity> getThreadsGroupByThreadStackCount(HttpServletRequest request) { - Map response = threadInfoApiService.getThreadsGroupByThreadStackCount(request); + @GetMapping(value = "/group-by/stack/names") + public ResponseEntity, List>> getThreadsGroupByStackThreadNames(HttpServletRequest request) { + Map, List> response = threadInfoApiService.getThreadsGroupByStackThreadNames(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/stack-thread/") - public ResponseEntity, List>> getThreadsGroupByStackThread(HttpServletRequest request) { - Map, List> response = threadInfoApiService.getThreadsGroupByStackThread(request); + @GetMapping(value = "/group-by/stack/count") + public ResponseEntity, Long>> getThreadsGroupByStackThreadCount(HttpServletRequest request) { + Map, Long> response = threadInfoApiService.getThreadsGroupByStackThreadCount(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/stack-thread/classes/") - public ResponseEntity, List>> getThreadsGroupByStackThreadClasses(HttpServletRequest request) { - Map, List> response = threadInfoApiService.getThreadsGroupByStackThreadClasses(request); + @GetMapping(value = "/states") + public ResponseEntity> getStates(HttpServletRequest request) { + List response = stateThreadInfoApiService.getStates(request); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); + } + + @GetMapping(value = "/states/{state}") + public ResponseEntity> getThreadsForState(@PathVariable(value = "state", required = true) Thread.State state, + HttpServletRequest request) { + List response = stateThreadInfoApiService.getThreadsForState(state, request); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); + } + + @GetMapping(value = "/states/{state}/classes") + public ResponseEntity> getThreadClassesForState(@PathVariable(value = "state", required = true) Thread.State state, + HttpServletRequest request) { + List response = stateThreadInfoApiService.getThreadClassesForState(state, request); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); + } + + @GetMapping(value = "/states/{state}/names") + public ResponseEntity> getThreadNamesForState(@PathVariable(value = "state", required = true) Thread.State state, + HttpServletRequest request) { + List response = stateThreadInfoApiService.getThreadNamesForState(state, request); + return new ResponseEntity<>(new ThreadInfoList<>(response), HttpStatus.OK); + } + + @GetMapping(value = "/group-by/states") + public ResponseEntity>> getThreadsGroupByState(HttpServletRequest request) { + Map> response = stateThreadInfoApiService.getThreadsGroupByState(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/stack-thread/names/") - public ResponseEntity, List>> getThreadsGroupByStackThreadNames(HttpServletRequest request) { - Map, List> response = threadInfoApiService.getThreadsGroupByStackThreadNames(request); + @GetMapping(value = "/group-by/states/classes") + public ResponseEntity>> getThreadClassesGroupByState(HttpServletRequest request) { + Map> response = stateThreadInfoApiService.getThreadClassesGroupByState(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/stack-thread/count/") - public ResponseEntity, Long>> getThreadsGroupByStackThreadCount(HttpServletRequest request) { - Map, Long> response = threadInfoApiService.getThreadsGroupByStackThreadCount(request); + @GetMapping(value = "/group-by/states/names") + public ResponseEntity>> getThreadNamesGroupByState(HttpServletRequest request) { + Map> response = stateThreadInfoApiService.getThreadNamesGroupByState(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @RestController - @RequestMapping(path = "/api/threads/states/") - public static class StateThreadInfoAPI { - - private final StateThreadInfoApiService stateThreadInfoApiService; - - public StateThreadInfoAPI(StateThreadInfoApiService stateThreadInfoApiService) { - this.stateThreadInfoApiService = stateThreadInfoApiService; - } - - @GetMapping(value = "/") - public ResponseEntity> getStates(HttpServletRequest request) { - List response = stateThreadInfoApiService.getStates(request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/state/{state}/") - public ResponseEntity> getThreadsForState(@PathVariable(value = "state", required = true) Thread.State state, - HttpServletRequest request) { - List response = stateThreadInfoApiService.getThreadsForState(state, request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/state/{state}/count/") - public ResponseEntity getThreadsCountForState(@PathVariable(value = "state", required = true) Thread.State state, - HttpServletRequest request) { - Long response = stateThreadInfoApiService.getThreadsCountForState(state, request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/state/{state}/classes/") - public ResponseEntity> getThreadClassesForState(@PathVariable(value = "state", required = true) Thread.State state, - HttpServletRequest request) { - List response = stateThreadInfoApiService.getThreadClassesForState(state, request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/state/{state}/names/") - public ResponseEntity> getThreadNamesForState(@PathVariable(value = "state", required = true) Thread.State state, - HttpServletRequest request) { - List response = stateThreadInfoApiService.getThreadNamesForState(state, request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/group-by/") - public ResponseEntity>> getThreadsGroupByState(HttpServletRequest request) { - Map> response = stateThreadInfoApiService.getThreadsGroupByState(request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/group-by/classes/") - public ResponseEntity>> getThreadClassesGroupByState(HttpServletRequest request) { - Map> response = stateThreadInfoApiService.getThreadClassesGroupByState(request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/group-by/names/") - public ResponseEntity>> getThreadNamesGroupByState(HttpServletRequest request) { - Map> response = stateThreadInfoApiService.getThreadNamesGroupByState(request); - return new ResponseEntity<>(response, HttpStatus.OK); - } - - @GetMapping(value = "/group-by/count/") - public ResponseEntity> getThreadsCountGroupByState(HttpServletRequest request) { - Map response = stateThreadInfoApiService.getThreadsCountGroupByState(request); - return new ResponseEntity<>(response, HttpStatus.OK); - } + @GetMapping(value = "/group-by/states/count") + public ResponseEntity> getThreadsCountGroupByState(HttpServletRequest request) { + Map response = stateThreadInfoApiService.getThreadsCountGroupByState(request); + return new ResponseEntity<>(response, HttpStatus.OK); } } diff --git a/src/org/scada_lts/web/mvc/api/ThreadInfoApiService.java b/src/org/scada_lts/web/mvc/api/ThreadInfoApiService.java index a19796da88..4d5ac4a9a6 100644 --- a/src/org/scada_lts/web/mvc/api/ThreadInfoApiService.java +++ b/src/org/scada_lts/web/mvc/api/ThreadInfoApiService.java @@ -56,54 +56,25 @@ public List getThreadNames(HttpServletRequest request) { } } - public Map getThreadsGroupByClassCount(HttpServletRequest request) { - checkIfNonAdminThenUnauthorized(request); - try { - return groupByAndSort(getThreadStack(), - Collectors.groupingBy(thread -> new Value(thread.getKey().getClass().getName()), Collectors.counting()), - Map.Entry.comparingByValue(Comparator.reverseOrder())); - } catch (Exception e) { - throw new InternalServerErrorException(e, request.getRequestURI()); - } - } - - public Map getThreadsGroupByThreadStack(HttpServletRequest request) { - checkIfNonAdminThenUnauthorized(request); - try { - return sorted(getThreadStack().entrySet().stream() - .collect(Collectors - .toMap(entry -> new ThreadInfo(entry.getKey()), entry -> - Stream.of(entry.getValue()) - .map(ThreadInfo.StackInfo::new) - .toArray(ThreadInfo.StackInfo[]::new)) - ), Comparator.comparing(entry -> entry.getValue().length, Comparator.reverseOrder())); - } catch (Exception e) { - throw new InternalServerErrorException(e, request.getRequestURI()); - } - } - - public Map getThreadsGroupByThreadStackClasses(HttpServletRequest request) { + public List getThreadClasses(HttpServletRequest request) { checkIfNonAdminThenUnauthorized(request); try { - return sorted(getThreadStack().entrySet().stream() - .collect(Collectors - .toMap(entry -> new ThreadInfo(entry.getKey()), entry -> - Stream.of(entry.getValue()) - .map(ThreadInfo.StackInfo::new) - .map(ThreadInfo.StackInfo::getClassName) - .toArray(String[]::new)) - ), Comparator.comparing(entry -> entry.getValue().length, Comparator.reverseOrder())); + return getThreadStack().keySet().stream() + .map(a -> a.getClass().getName()) + .map(Value::new) + .sorted(Comparator.comparing(Value::getValue)) + .collect(Collectors.toList()); } catch (Exception e) { throw new InternalServerErrorException(e, request.getRequestURI()); } } - public Map getThreadsGroupByThreadStackCount(HttpServletRequest request) { + public Map getThreadsGroupByClassCount(HttpServletRequest request) { checkIfNonAdminThenUnauthorized(request); try { - return sorted(getThreadStack().entrySet().stream().collect(Collectors - .toMap(entry -> new ThreadInfo(entry.getKey()), entry -> entry.getValue().length)), - Comparator.comparing(entry -> entry.getValue(), Comparator.reverseOrder())); + return groupByAndSort(getThreadStack(), + Collectors.groupingBy(thread -> new Value(thread.getKey().getClass().getName()), Collectors.counting()), + Map.Entry.comparingByValue(Comparator.reverseOrder())); } catch (Exception e) { throw new InternalServerErrorException(e, request.getRequestURI()); } diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java index f54955d483..d086e9502e 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java @@ -24,153 +24,153 @@ public WorkItemInfoAPI(WorkItemInfoApiService workItemInfoApiService) { this.workItemInfoApiService = workItemInfoApiService; } - @GetMapping(value = "/") + @GetMapping(value = "") public ResponseEntity getNotExecutedWorkItems(HttpServletRequest request) { List response = workItemInfoApiService.getNotExecutedWorkItems(request); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/group-by/") + @GetMapping(value = "/group-by-classes") public ResponseEntity>> getNotExecutedWorkItemsGroupBy(HttpServletRequest request) { Map> response = workItemInfoApiService.getNotExecutedWorkItemsGroupBy(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/count/") - public ResponseEntity> getNotExecutedWorkItemsGroupByCount(HttpServletRequest request) { - Map response = workItemInfoApiService.getNotExecutedWorkItemsGroupByCount(request); + @GetMapping(value = "/group-by-classes/count") + public ResponseEntity> getNotExecutedWorkItemsGroupByClassName(HttpServletRequest request) { + Map response = workItemInfoApiService.getNotExecutedWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/executed/") + @GetMapping(value = "/executed") public ResponseEntity getExecutedWorkItems(HttpServletRequest request) { List response = workItemInfoApiService.getExecutedWorkItems(request); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/executed/group-by/") + @GetMapping(value = "/executed/group-by-classes") public ResponseEntity>> getExecutedWorkItemsGroupBy(HttpServletRequest request) { Map> response = workItemInfoApiService.getExecutedWorkItemsGroupBy(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/executed/group-by/count/") - public ResponseEntity> getExecutedWorkItemsGroupByCount(HttpServletRequest request) { - Map response = workItemInfoApiService.getExecutedWorkItemsGroupByCount(request); + @GetMapping(value = "/executed/group-by-classes/count") + public ResponseEntity> getExecutedWorkItemsGroupByClassName(HttpServletRequest request) { + Map response = workItemInfoApiService.getExecutedWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/success/") + @GetMapping(value = "/success") public ResponseEntity getExecutedSuccessWorkItems(HttpServletRequest request) { List response = workItemInfoApiService.getExecutedSuccessWorkItems(request); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/success/group-by/") + @GetMapping(value = "/success/group-by-classes") public ResponseEntity>> getExecutedSuccessWorkItemsGroupBy(HttpServletRequest request) { Map> response = workItemInfoApiService.getExecutedSuccessWorkItemsGroupBy(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/success/group-by/count/") + @GetMapping(value = "/success/group-by-classes/count") public ResponseEntity> getExecutedSuccessWorkItemsGroupByCount(HttpServletRequest request) { - Map response = workItemInfoApiService.getExecutedSuccessWorkItemsGroupByCount(request); + Map response = workItemInfoApiService.getExecutedSuccessWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/failed/") + @GetMapping(value = "/failed") public ResponseEntity getExecutedFailWorkItems(HttpServletRequest request) { List response = workItemInfoApiService.getExecutedFailedWorkItems(request); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/failed/group-by/") + @GetMapping(value = "/failed/group-by-classes") public ResponseEntity>> getExecutedFailWorkItemsGroupBy(HttpServletRequest request) { Map> response = workItemInfoApiService.getExecutedFailedWorkItemsGroupBy(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/failed/group-by/count/") + @GetMapping(value = "/failed/group-by-classes/count") public ResponseEntity> getExecutedFailWorkItemsGroupByCount(HttpServletRequest request) { - Map response = workItemInfoApiService.getExecutedFailedWorkItemsGroupByCount(request); + Map response = workItemInfoApiService.getExecutedFailedWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/running/") + @GetMapping(value = "/running") public ResponseEntity getRunningWorkItems(HttpServletRequest request) { List response = workItemInfoApiService.getRunningWorkItems(request); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/running/group-by/") + @GetMapping(value = "/running/group-by-classes") public ResponseEntity>> getRunningFailWorkItemsGroupBy(HttpServletRequest request) { Map> response = workItemInfoApiService.getRunningWorkItemsGroupBy(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/running/group-by/count/") + @GetMapping(value = "/running/group-by-classes/count") public ResponseEntity> getRunningFailWorkItemsGroupByCount(HttpServletRequest request) { - Map response = workItemInfoApiService.getRunningWorkItemsGroupByCount(request); + Map response = workItemInfoApiService.getRunningWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/all/") + @GetMapping(value = "/all") public ResponseEntity getWorkItems(HttpServletRequest request) { List response = workItemInfoApiService.getWorkItems(request); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/all/group-by/") + @GetMapping(value = "/all/group-by-classes") public ResponseEntity>> getWorkItemsGroupBy(HttpServletRequest request) { Map> response = workItemInfoApiService.getWorkItemsGroupBy(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/all/group-by/count/") + @GetMapping(value = "/all/group-by-classes/count") public ResponseEntity> getWorkItemsGroupByCount(HttpServletRequest request) { - Map response = workItemInfoApiService.getWorkItemsGroupByCount(request); + Map response = workItemInfoApiService.getWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/longer/{executedMs}/") + @GetMapping(value = "/longer/{executedMs}") public ResponseEntity getExecutedLongerWorkItems(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { List response = workItemInfoApiService.getExecutedLongerWorkItems(request, executedMs, false); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/longer/{executedMs}/group-by/") + @GetMapping(value = "/longer/{executedMs}/group-by-classes") public ResponseEntity>> getExecutedLongerWorkItemsGroupBy(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { Map> response = workItemInfoApiService.getExecutedLongerWorkItemsGroupBy(request, executedMs, false); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/longer/{executedMs}/group-by/count/") + @GetMapping(value = "/longer/{executedMs}/group-by-classes/count") public ResponseEntity> getExecutedLongerWorkItemsGroupByCount(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { - Map response = workItemInfoApiService.getExecutedLongerWorkItemsGroupByCount(request, executedMs, false); + Map response = workItemInfoApiService.getExecutedLongerWorkItemsGroupByClassName(request, executedMs, false); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/longer/{executedMs}/history/") + @GetMapping(value = "/history/longer/{executedMs}") public ResponseEntity getExecutedLongerWorkItemsHistory(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { List response = workItemInfoApiService.getExecutedLongerWorkItems(request, executedMs, true); return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/longer/{executedMs}/history/group-by/") + @GetMapping(value = "/history/longer/{executedMs}/group-by-classes") public ResponseEntity>> getExecutedLongerWorkItemsGroupByHistory(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { Map> response = workItemInfoApiService.getExecutedLongerWorkItemsGroupBy(request, executedMs, true); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/longer/{executedMs}/history/group-by/count/") + @GetMapping(value = "/history/longer/{executedMs}/group-by-classes/count") public ResponseEntity> getExecutedLongerWorkItemsGroupByCountHistory(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { - Map response = workItemInfoApiService.getExecutedLongerWorkItemsGroupByCount(request, executedMs, true); + Map response = workItemInfoApiService.getExecutedLongerWorkItemsGroupByClassName(request, executedMs, true); return new ResponseEntity<>(response, HttpStatus.OK); } @@ -181,17 +181,17 @@ public ResponseEntity getExecutedLessWorkItems(HttpServletRequ return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/less/{executedMs}/group-by/") + @GetMapping(value = "/less/{executedMs}/group-by-classes") public ResponseEntity>> getExecutedLessWorkItemsGroupBy(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { Map> response = workItemInfoApiService.getExecutedLessWorkItemsGroupBy(request, executedMs); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/less/{executedMs}/group-by/count/") + @GetMapping(value = "/less/{executedMs}/group-by-classes/count") public ResponseEntity> getExecutedLessWorkItemsGroupByCount(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { - Map response = workItemInfoApiService.getExecutedLessWorkItemsGroupByCount(request, executedMs); + Map response = workItemInfoApiService.getExecutedLessWorkItemsGroupByClassName(request, executedMs); return new ResponseEntity<>(response, HttpStatus.OK); } @@ -202,17 +202,17 @@ public ResponseEntity getExecutedWorkItemsByPriority(HttpServl return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); } - @GetMapping(value = "/priority/{priority}/group-by/") + @GetMapping(value = "/priority/{priority}/group-by-classes") public ResponseEntity>> getExecutedLessWorkItemsGroupByPriority(HttpServletRequest request, @PathVariable("priority") WorkItemPriority priority) { Map> response = workItemInfoApiService.getExecutedLessWorkItemsGroupByPriority(request, priority); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/priority/{priority}/group-by/count/") + @GetMapping(value = "/priority/{priority}/group-by-classes/count") public ResponseEntity> getExecutedLessWorkItemsGroupByPriorityCount(HttpServletRequest request, @PathVariable("priority") WorkItemPriority priority) { - Map response = workItemInfoApiService.getExecutedLessWorkItemsGroupByPriorityCount(request, priority); + Map response = workItemInfoApiService.getExecutedLessWorkItemsGroupByPriorityClassName(request, priority); return new ResponseEntity<>(response, HttpStatus.OK); } @@ -227,38 +227,44 @@ public ScheduledWorkItemInfoAPI(ScheduledWorkItemInfoApiService scheduledWorkIte } @GetMapping(value = "/") - public ResponseEntity getScheduledWorkItems(HttpServletRequest request) { + public ResponseEntity> getScheduledWorkItems(HttpServletRequest request) { List response = scheduledWorkItemInfoApiService.getScheduledWorkItems(request); - return new ResponseEntity<>(new ScheduledWorkItemInfoList(response), HttpStatus.OK); + return new ResponseEntity<>(new ScheduledWorkItemInfoList<>(response), HttpStatus.OK); } - @GetMapping(value = "/group-by/") + @GetMapping(value = "/group-by-classes") public ResponseEntity>> getScheduledWorkItemsGroupByClassName(HttpServletRequest request) { Map> response = scheduledWorkItemInfoApiService.getScheduledWorkItemsGroupByClassName(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/group-by/count/") + @GetMapping(value = "/group-by-classes/count") public ResponseEntity> getScheduledWorkItemsGroupByClassNameCount(HttpServletRequest request) { Map response = scheduledWorkItemInfoApiService.getScheduledWorkItemsGroupByClassNameCount(request); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/state/{state}/") - public ResponseEntity getScheduledWorkItems(HttpServletRequest request, + @GetMapping(value = "/states") + public ResponseEntity> getScheduledWorkItemsStates(HttpServletRequest request) { + List response = scheduledWorkItemInfoApiService.getScheduledWorkItemsStates(request); + return new ResponseEntity<>(new ScheduledWorkItemInfoList<>(response), HttpStatus.OK); + } + + @GetMapping(value = "/states/{state}") + public ResponseEntity> getScheduledWorkItems(HttpServletRequest request, @PathVariable("state") TimerTaskState state) { List response = scheduledWorkItemInfoApiService.getScheduledWorkItems(request, state); - return new ResponseEntity<>(new ScheduledWorkItemInfoList(response), HttpStatus.OK); + return new ResponseEntity<>(new ScheduledWorkItemInfoList<>(response), HttpStatus.OK); } - @GetMapping(value = "/state/{state}/group-by/") + @GetMapping(value = "/states/{state}/group-by-classes") public ResponseEntity>> getScheduledWorkItemsGroupByClassName(HttpServletRequest request, @PathVariable("state")TimerTaskState state) { Map> response = scheduledWorkItemInfoApiService.getScheduledWorkItemsGroupByClassName(request, state); return new ResponseEntity<>(response, HttpStatus.OK); } - @GetMapping(value = "/state/{state}/group-by/count/") + @GetMapping(value = "/states/{state}/group-by-classes/count") public ResponseEntity> getScheduledWorkItemsGroupByClassNameCount(HttpServletRequest request, @PathVariable("state")TimerTaskState state) { Map response = scheduledWorkItemInfoApiService.getScheduledWorkItemsGroupByClassNameCount(request, state); diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java index 1d74c4e916..1c1bd2489b 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java @@ -59,27 +59,27 @@ public List getExecutedLessWorkItems(HttpServletRequest request, i WorkItemsUtils::getByExecutedLessThan, executedMs); } - public Map getExecutedWorkItemsGroupByCount(HttpServletRequest request) { + public Map getExecutedWorkItemsGroupByClassName(HttpServletRequest request) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getByExecuted); } - public Map getExecutedSuccessWorkItemsGroupByCount(HttpServletRequest request) { + public Map getExecutedSuccessWorkItemsGroupByClassName(HttpServletRequest request) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getBySuccess); } - public Map getExecutedFailedWorkItemsGroupByCount(HttpServletRequest request) { + public Map getExecutedFailedWorkItemsGroupByClassName(HttpServletRequest request) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getByFailed); } - public Map getWorkItemsGroupByCount(HttpServletRequest request) { + public Map getWorkItemsGroupByClassName(HttpServletRequest request) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getAll); } - public Map getNotExecutedWorkItemsGroupByCount(HttpServletRequest request) { + public Map getNotExecutedWorkItemsGroupByClassName(HttpServletRequest request) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getByNotExecuted); } - public Map getExecutedLongerWorkItemsGroupByCount(HttpServletRequest request, int executedMs, boolean history) { + public Map getExecutedLongerWorkItemsGroupByClassName(HttpServletRequest request, int executedMs, boolean history) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, byExecuteMsComparator(), executed -> { if(history) @@ -88,18 +88,18 @@ public Map getExecutedLongerWorkItemsGroupByCount(HttpServletReque }, executedMs); } - public Map getExecutedLessWorkItemsGroupByCount(HttpServletRequest request, int executedMs) { + public Map getExecutedLessWorkItemsGroupByClassName(HttpServletRequest request, int executedMs) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, byExecuteMsComparator(), WorkItemsUtils::getByExecutedLessThan, executedMs); } - public Map getExecutedLessWorkItemsGroupByPriorityCount(HttpServletRequest request, - WorkItemPriority priority) { + public Map getExecutedLessWorkItemsGroupByPriorityClassName(HttpServletRequest request, + WorkItemPriority priority) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, byExecuteMsComparator(), WorkItemsUtils::getByPriority, priority); } - public Map getRunningWorkItemsGroupByCount(HttpServletRequest request) { + public Map getRunningWorkItemsGroupByClassName(HttpServletRequest request) { return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getRunning); } diff --git a/src/org/scada_lts/web/mvc/api/json/ScheduledWorkItemInfoList.java b/src/org/scada_lts/web/mvc/api/json/ScheduledWorkItemInfoList.java index b53fbacae3..0387e7dea6 100644 --- a/src/org/scada_lts/web/mvc/api/json/ScheduledWorkItemInfoList.java +++ b/src/org/scada_lts/web/mvc/api/json/ScheduledWorkItemInfoList.java @@ -2,16 +2,16 @@ import java.util.List; -public class ScheduledWorkItemInfoList { +public class ScheduledWorkItemInfoList { private final int size; - private final List workItemScheduled; + private final List workItemScheduled; - public ScheduledWorkItemInfoList(List workItemScheduled) { + public ScheduledWorkItemInfoList(List workItemScheduled) { this.workItemScheduled = workItemScheduled; this.size = workItemScheduled.size(); } - public List getWorkItemScheduled() { + public List getWorkItemScheduled() { return workItemScheduled; } diff --git a/src/org/scada_lts/web/mvc/api/json/ThreadInfoList.java b/src/org/scada_lts/web/mvc/api/json/ThreadInfoList.java new file mode 100644 index 0000000000..9896539afe --- /dev/null +++ b/src/org/scada_lts/web/mvc/api/json/ThreadInfoList.java @@ -0,0 +1,24 @@ +package org.scada_lts.web.mvc.api.json; + + +import java.util.List; + +public class ThreadInfoList { + + private final int size; + private final List threadInfoList; + + public ThreadInfoList(List threadInfoList) { + this.threadInfoList = threadInfoList; + this.size = threadInfoList.size(); + } + + public List getThreadInfoList() { + return threadInfoList; + } + + public int getSize() { + return size; + } +} + diff --git a/webapp-resources/env.properties b/webapp-resources/env.properties index 70ca032a42..2c2c4b9b82 100644 --- a/webapp-resources/env.properties +++ b/webapp-resources/env.properties @@ -131,4 +131,5 @@ scadalts.security.js.access.granted.class.regexes=^.* view.forceFullScreen=false view.hideShortcutDisableFullScreen=false eventdetector.cache.enabled=true -event.pending.limit=101 \ No newline at end of file +event.pending.limit=101 +thread.name.additional.length=255 \ No newline at end of file From 3fb887a38c074405cdb7471f9fc19831c39352ed Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 14 Sep 2023 20:41:29 +0200 Subject: [PATCH 19/43] #2698 Corrected cache pending events - - Update cached pending events for only logged user; (Limiting the number of database queries) - added spring bean org.scada_lts.login.LoggedUsers; corrected update permission without logout user; added test suite: LoggedUserTestsSuite (LoggedUsersMultiThreadTest, LoggedUsersTest), extends MultiThreadEngine and TestConcurrentUtils; removed deprecated methods: MangoEvent/EventService.getPendingSimpleEventsForDataSource, MangoEvent/EventService.getPendingSimpleEvents; removed deprecated class EventDao; added enum AlarmLevelType; refactor: PendingEventsDAO, PendingEventService; - The current limited event analysis to the number of events within the limit, this has been corrected for cache mode; (Information about events could be false, e.g. an event that should have been displayed could be missing, on Alarm List Component) - added parameter event.pending.update.limit to env.properties, corrected AlarmListComponent, added method getEventPendingUpdateLimit in SystemSettingsUtils; - By default, the Pending event cache is enabled; (This setting can be changed from SystemSettings) - set abilit.cacheEnable=true in env.properties --- WebContent/WEB-INF/applicationContext.xml | 2 + build.gradle | 1 + .../view/component/AlarmListComponent.java | 2 +- .../org/scadabr/web/dwr/UsersProfilesDwr.java | 3 + src/com/serotonin/mango/db/dao/EventDao.java | 221 -------- .../mango/rt/event/type/AlarmLevelType.java | 32 ++ src/com/serotonin/mango/vo/User.java | 28 +- src/com/serotonin/mango/web/dwr/UsersDwr.java | 7 +- src/org/scada_lts/dao/PendingEventsDAO.java | 17 +- src/org/scada_lts/login/ILoggedUsers.java | 15 + src/org/scada_lts/login/LoggedUsers.java | 122 ++++ .../scada_lts/mango/adapter/MangoEvent.java | 6 - .../scada_lts/mango/service/EventService.java | 40 +- .../mango/service/PendingEventService.java | 63 ++- .../scada_lts/utils/SystemSettingsUtils.java | 15 +- .../scada_lts/web/beans/ApplicationBeans.java | 6 + .../mango/vo/LoggedUserTestsSuite.java | 17 + .../mango/vo/LoggedUsersMultiThreadTest.java | 532 +++++++++++++++++ .../serotonin/mango/vo/LoggedUsersTest.java | 535 ++++++++++++++++++ .../MessagingChannelsMultiThreadTest.java | 28 +- test/utils/MultiThreadEngine.java | 20 +- test/utils/TestConcurrentUtils.java | 55 +- test/utils/mock/EventServiceMock.java | 10 - webapp-resources/env.properties | 3 +- 24 files changed, 1448 insertions(+), 332 deletions(-) delete mode 100644 src/com/serotonin/mango/db/dao/EventDao.java create mode 100644 src/com/serotonin/mango/rt/event/type/AlarmLevelType.java create mode 100644 src/org/scada_lts/login/ILoggedUsers.java create mode 100644 src/org/scada_lts/login/LoggedUsers.java create mode 100644 test/com/serotonin/mango/vo/LoggedUserTestsSuite.java create mode 100644 test/com/serotonin/mango/vo/LoggedUsersMultiThreadTest.java create mode 100644 test/com/serotonin/mango/vo/LoggedUsersTest.java diff --git a/WebContent/WEB-INF/applicationContext.xml b/WebContent/WEB-INF/applicationContext.xml index 57cbc58713..d0e68ab512 100644 --- a/WebContent/WEB-INF/applicationContext.xml +++ b/WebContent/WEB-INF/applicationContext.xml @@ -244,4 +244,6 @@ + + diff --git a/build.gradle b/build.gradle index e577577d05..6921d48ecb 100644 --- a/build.gradle +++ b/build.gradle @@ -197,5 +197,6 @@ test { includeTestsMatching "org.scada_lts.dao.IsEventDetectorXidUniqueTest" includeTestsMatching "com.serotonin.mango.view.export.CsvWriterTest" includeTestsMatching "com.serotonin.mango.util.EmailValidatorTest" + includeTestsMatching "com.serotonin.mango.vo.LoggedUserTestsSuite" } } \ No newline at end of file diff --git a/src/br/org/scadabr/view/component/AlarmListComponent.java b/src/br/org/scadabr/view/component/AlarmListComponent.java index 6241cf93af..b60cb78fc8 100644 --- a/src/br/org/scadabr/view/component/AlarmListComponent.java +++ b/src/br/org/scadabr/view/component/AlarmListComponent.java @@ -58,7 +58,7 @@ public String generateContent() { WebContext webContext = WebContextFactory.get(); HttpServletRequest request = webContext.getHttpServletRequest(); List toViewEvents = new EventService().getPendingEventsAlarmLevelMin(Common - .getUser().getId(), minAlarmLevel, maxListSize, true); + .getUser().getId(), minAlarmLevel, maxListSize); model.put("nome", "marlon"); model.put("events",toViewEvents); diff --git a/src/br/org/scadabr/web/dwr/UsersProfilesDwr.java b/src/br/org/scadabr/web/dwr/UsersProfilesDwr.java index f3426259a2..3c918d3f44 100644 --- a/src/br/org/scadabr/web/dwr/UsersProfilesDwr.java +++ b/src/br/org/scadabr/web/dwr/UsersProfilesDwr.java @@ -10,6 +10,7 @@ import javax.servlet.http.HttpServletRequest; +import com.serotonin.mango.vo.User; import org.directwebremoting.WebContextFactory; import br.org.scadabr.api.exception.DAOException; @@ -33,6 +34,7 @@ import org.scada_lts.dao.model.ScadaObjectIdentifier; import org.scada_lts.mango.service.UsersProfileService; import org.scada_lts.mango.service.ViewService; +import org.scada_lts.web.beans.ApplicationBeans; public class UsersProfilesDwr { @@ -122,6 +124,7 @@ public DwrResponseI18n saveUserAdmin(int id, String name, try { usersProfileService.saveUsersProfile(profile); + ApplicationBeans.getLoggedUsersBean().updateUsers(profile); } catch (DAOException e) { response.addMessage(new LocalizableMessage( "userProfiles.validate.nameUnique")); diff --git a/src/com/serotonin/mango/db/dao/EventDao.java b/src/com/serotonin/mango/db/dao/EventDao.java deleted file mode 100644 index 9da777843c..0000000000 --- a/src/com/serotonin/mango/db/dao/EventDao.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * (c) 2016 Abil'I.T. http://abilit.eu/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.serotonin.mango.db.dao; - -import java.util.Date; -import java.util.List; -import java.util.ResourceBundle; - -import org.scada_lts.dao.DAO; -import org.scada_lts.mango.adapter.MangoEvent; -import org.scada_lts.mango.service.EventService; - -import com.serotonin.mango.rt.event.EventInstance; -import com.serotonin.mango.rt.event.type.EventType; -import com.serotonin.mango.vo.UserComment; -import com.serotonin.mango.vo.event.EventHandlerVO; -import com.serotonin.mango.vo.event.EventTypeVO; - -/** - * Rewrite (ultimately to remove) and only use EventService - * - * @author Grzesiek Bylica Abil'I.T. development team, sdt@abilit.eu - */ -@Deprecated -public class EventDao { - - private MangoEvent eventService = new EventService(); - - public void saveEvent(EventInstance event) { - eventService.saveEvent(event); - } - - public void ackEvent(int eventId, long time, int userId, - int alternateAckSource, boolean signalAlarmLevelChange) { - eventService.ackEvent(eventId, time, userId, alternateAckSource); - } - - public void ackEvent(int eventId, long time, int userId, - int alternateAckSource) { - eventService.ackEvent(eventId, time, userId, alternateAckSource); - } - - public void insertUserEvents(final int eventId, - final List userIds, final boolean alarm) { - eventService.insertUserEvents(eventId, userIds, alarm); - } - - public List getActiveEvents() { - return eventService.getActiveEvents(); - } - - public List getEventsForDataPoint(int dataPointId, int userId) { - return eventService.getEventsForDataPoint(dataPointId, userId); - } - - public List getPendingEventsForDataPoint(int dataPointId, - int userId) { - return eventService.getPendingEventsForDataPoint(dataPointId, userId); - } - - public List getPendingEventsForDataSource(int dataSourceId, - int userId) { - return eventService.getPendingEvents(EventType.EventSources.DATA_SOURCE, - dataSourceId, userId); - } - - public List getPendingEventsForPublisher(int publisherId, - int userId) { - return eventService.getPendingEvents(EventType.EventSources.PUBLISHER, publisherId, - userId); - } - - public List getPendingEvents(int userId) { - return eventService.getPendingEvents(userId); - } - - public void attachRelationalInfo(List list) { - eventService.attachRelationalInfo(list); - } - - public EventInstance insertEventComment(int eventId, UserComment comment) { - return eventService.insertEventComment(eventId, comment); - } - - public int purgeEventsBefore(final long time) { - return eventService.purgeEventsBefore(time); - } - - public int getEventCount() { - return eventService.getEventCount(); - } - - public List searchOld(int eventId, int eventSourceType, - String status, int alarmLevel, final String[] keywords, - final int maxResults, int userId, final ResourceBundle bundle) { - return eventService.searchOld(eventId, eventSourceType, status, alarmLevel, keywords, maxResults, userId, bundle); - - } - - public List search(int eventId, int eventSourceType, - String status, int alarmLevel, final String[] keywords, int userId, - final ResourceBundle bundle, final int from, final int to, - final Date date) { - return eventService.search(eventId, eventSourceType, status, alarmLevel, keywords, userId, bundle, from, to, date); - - } - - public List search(int eventId, int eventSourceType, - String status, int alarmLevel, final String[] keywords, - long dateFrom, long dateTo, int userId, - final ResourceBundle bundle, final int from, final int to, - final Date date) { - - return eventService.search(eventId, eventSourceType, status, alarmLevel, keywords, dateFrom, dateTo, userId, bundle, from, to, date); - } - - public int getSearchRowCount() { - return eventService.getSearchRowCount(); - } - - public int getStartRow() { - return eventService.getStartRow(); - } - - // - // / - // / Event handlers - // / - // - public String generateUniqueXid() { - return DAO.getInstance().generateUniqueXid(EventHandlerVO.XID_PREFIX, "eventHandlers"); - } - - public boolean isXidUnique(String xid, int excludeId) { - return DAO.getInstance().isXidUnique(xid, excludeId, "eventHandlers"); - } - - public EventType getEventHandlerType(int handlerId) { - return eventService.getEventHandlerType(handlerId); - } - - public List getEventHandlers(EventType type) { - return eventService.getEventHandlers(type); - } - - public List getEventHandlers(EventTypeVO type) { - return eventService.getEventHandlers(type); - } - - public List getEventHandlers() { - return eventService.getEventHandlers(); - } - - public EventHandlerVO getEventHandler(int eventHandlerId) { - return eventService.getEventHandler(eventHandlerId); - } - - public EventHandlerVO getEventHandler(String xid) { - return eventService.getEventHandler(xid); - } - - public EventHandlerVO saveEventHandler(final EventType type, - final EventHandlerVO handler) { - return eventService.saveEventHandler(type, handler); - } - - public EventHandlerVO saveEventHandler(final EventTypeVO type, - final EventHandlerVO handler) { - return eventService.saveEventHandler(type, handler); - } - - public void deleteEventHandler(final int handlerId) { - eventService.deleteEventHandler(handlerId); - } - - public boolean toggleSilence(int eventId, int userId) { - return eventService.toggleSilence(eventId, userId); - } - - @Deprecated - public int getHighestUnsilencedAlarmLevel(int userId) { - return eventService.getHighestUnsilencedAlarmLevel(userId); - } - - public static List getFromCache(int userId) { - return EventService.getFromCache(userId); - } - - public static void addToCache(int userId, List list) { - EventService.addToCache(userId, list); - } - - public static void updateCache(EventInstance event) { - EventService.updateCache(event); - } - - public static void removeUserIdFromCache(int userId) { - EventService.removeUserIdFromCache(userId); - } - - public static void clearCache() { - EventService.clearCache(); - } - - -} diff --git a/src/com/serotonin/mango/rt/event/type/AlarmLevelType.java b/src/com/serotonin/mango/rt/event/type/AlarmLevelType.java new file mode 100644 index 0000000000..4f6e9dfb32 --- /dev/null +++ b/src/com/serotonin/mango/rt/event/type/AlarmLevelType.java @@ -0,0 +1,32 @@ +package com.serotonin.mango.rt.event.type; + +import com.serotonin.mango.rt.event.AlarmLevels; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum AlarmLevelType { + + NONE(AlarmLevels.NONE), + INFORMATION(AlarmLevels.INFORMATION), + URGENT(AlarmLevels.URGENT), + CRITICAL(AlarmLevels.CRITICAL), + LIFE_SAFETY(AlarmLevels.LIFE_SAFETY); + private final int code; + AlarmLevelType(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public static List getAlarmLevels() { + return Stream.of(AlarmLevelType.values()).collect(Collectors.toList()); + } + + public static List getAlarmLevelsWithoutNone() { + return Stream.of(AlarmLevelType.values()).filter(a -> a != AlarmLevelType.NONE).collect(Collectors.toList()); + } +} diff --git a/src/com/serotonin/mango/vo/User.java b/src/com/serotonin/mango/vo/User.java index aeccfb4798..b89b766bdd 100644 --- a/src/com/serotonin/mango/vo/User.java +++ b/src/com/serotonin/mango/vo/User.java @@ -21,6 +21,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import br.org.scadabr.vo.exporter.ZIPProjectManager; @@ -51,6 +52,7 @@ import com.serotonin.web.i18n.LocalizableMessage; import org.scada_lts.dao.UsersProfileDAO; import org.scada_lts.mango.service.UsersProfileService; +import org.scada_lts.web.beans.ApplicationBeans; @JsonRemoteEntity public class User implements SetPointSource, HttpSessionBindingListener, @@ -123,18 +125,6 @@ public class User implements SetPointSource, HttpSessionBindingListener, public User() { } - @Deprecated - public User(int id, String username, String email, String phone, boolean admin, boolean disabled, String homeUrl, long lastLogin) { - this.id = id; - this.username = username; - this.email = email; - this.phone = phone; - this.admin = admin; - this.disabled = disabled; - this.homeUrl = homeUrl; - this.lastLogin = lastLogin; - } - public User(int id, String username, String firstName, String lastName, String email, String phone, boolean admin, boolean disabled, String homeUrl, long lastLogin) { this.id = id; this.username = username; @@ -231,6 +221,16 @@ public void raiseRecursionFailureEvent() { throw new ShouldNeverHappenException(""); } + @Override + public void valueBound(HttpSessionBindingEvent event) { + ApplicationBeans.getLoggedUsersBean().addUser(this, event.getSession()); + } + + @Override + public void valueUnbound(HttpSessionBindingEvent event) { + ApplicationBeans.getLoggedUsersBean().removeUser(this, event.getSession()); + } + // Convenience method for JSPs @JsonIgnore public boolean isDataSourcePermission() { @@ -674,6 +674,10 @@ public int getUserProfile() { public void resetUserProfile() { this.userProfile = Common.NEW_ID; + this.dataPointProfilePermissions.clear(); + this.dataSourceProfilePermissions.clear(); + this.watchListProfilePermissions.clear(); + this.viewProfilePermissions.clear(); } @Override diff --git a/src/com/serotonin/mango/web/dwr/UsersDwr.java b/src/com/serotonin/mango/web/dwr/UsersDwr.java index 0e65e0f71e..f670587539 100644 --- a/src/com/serotonin/mango/web/dwr/UsersDwr.java +++ b/src/com/serotonin/mango/web/dwr/UsersDwr.java @@ -51,6 +51,7 @@ import org.scada_lts.mango.service.SystemSettingsService; import org.scada_lts.mango.service.UserService; import org.scada_lts.mango.service.UsersProfileService; +import org.scada_lts.web.beans.ApplicationBeans; import org.scada_lts.web.mvc.api.json.JsonSettingsMisc; import static com.serotonin.mango.util.LoggingUtils.userInfo; @@ -215,10 +216,10 @@ else if (dupUser != null && id != dupUser.getId()) // set permission on all watchlists } - if (currentUser.getId() == id) + /*if (currentUser.getId() == id) // Update the user object in session too. Why not? - Common.updateUserInSession(request, user); - + Common.updateUserInSession(request, user);*/ + ApplicationBeans.getLoggedUsersBean().updateUser(user); response.addData("userId", user.getId()); } diff --git a/src/org/scada_lts/dao/PendingEventsDAO.java b/src/org/scada_lts/dao/PendingEventsDAO.java index a23dfe6fab..14f19b659c 100644 --- a/src/org/scada_lts/dao/PendingEventsDAO.java +++ b/src/org/scada_lts/dao/PendingEventsDAO.java @@ -19,9 +19,11 @@ import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.List; import java.util.Map; +import com.serotonin.mango.rt.event.type.AlarmLevelType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.scada_lts.utils.EventTypeUtil; @@ -85,31 +87,32 @@ public class PendingEventsDAO { + "left join userEvents ue on e.id=ue.eventId " + "where " + "ue.userId=? and " - + "(e.ackTs is null or e.ackTs = 0) " + + "(e.ackTs is null or e.ackTs = 0) and " + + "e.alarmLevel >= ? " + "order by e.activeTs desc " - + "LIMIT ? "; + + "LIMIT ? OFFSET ?"; // @formatter:on - public List getPendingEvents(int userId, final Map> comments) { - return getPendingEvents(userId, comments, 100); + public List getPendingEvents(int userId, final Map> comments, AlarmLevelType minAlarmLevel) { + return getPendingEvents(userId, comments, minAlarmLevel, 100, 0); } - public List getPendingEvents(int userId, final Map> comments, int limit) { + public List getPendingEvents(int userId, final Map> comments, AlarmLevelType minAlarmLevel, int limit, int offset) { if (LOG.isTraceEnabled()) { LOG.trace("SQL PendingEvents userId:"+userId); } try { @SuppressWarnings({ "unchecked", "rawtypes" }) - List listEvents = DAO.getInstance().getJdbcTemp().query(SQL_EVENTS,new Integer[]{userId, limit}, + List listEvents = DAO.getInstance().getJdbcTemp().query(SQL_EVENTS,new Integer[]{userId, minAlarmLevel.getCode(), limit, offset}, (rs, rownumber) -> mapToEvent(comments, rs)); return listEvents; } catch (Exception e) { LOG.error(e); } - return null; + return Collections.emptyList(); } private EventInstance mapToEvent(Map> comments, ResultSet rs) throws SQLException { diff --git a/src/org/scada_lts/login/ILoggedUsers.java b/src/org/scada_lts/login/ILoggedUsers.java new file mode 100644 index 0000000000..90b32a2970 --- /dev/null +++ b/src/org/scada_lts/login/ILoggedUsers.java @@ -0,0 +1,15 @@ +package org.scada_lts.login; + +import br.org.scadabr.vo.usersProfiles.UsersProfileVO; +import com.serotonin.mango.vo.User; + +import javax.servlet.http.HttpSession; +import java.util.Set; + +public interface ILoggedUsers { + User addUser(User user, HttpSession session); + void updateUser(User user); + void updateUsers(UsersProfileVO profile); + User removeUser(User user, HttpSession session); + Set getUserIds(); +} diff --git a/src/org/scada_lts/login/LoggedUsers.java b/src/org/scada_lts/login/LoggedUsers.java new file mode 100644 index 0000000000..18eb58153e --- /dev/null +++ b/src/org/scada_lts/login/LoggedUsers.java @@ -0,0 +1,122 @@ +package org.scada_lts.login; + +import br.org.scadabr.vo.usersProfiles.UsersProfileVO; +import com.serotonin.mango.util.LoggingUtils; +import com.serotonin.mango.vo.User; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.security.core.GrantedAuthority; + +import javax.servlet.http.HttpSession; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static com.serotonin.mango.Common.SESSION_USER; + +public class LoggedUsers implements ILoggedUsers { + + private static final Log LOG = LogFactory.getLog(LoggedUsers.class); + + public LoggedUsers() {} + + private final Map loggedUsers = new ConcurrentHashMap<>(); + private final Map> loggedSessions = new ConcurrentHashMap<>(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final ThreadLocal blocked = new ThreadLocal<>(); + + @Override + public User addUser(User user, HttpSession session) { + lock.writeLock().lock(); + try { + if("setAttribute".equals(blocked.get())) { + LOG.warn("blocked addUser: " + LoggingUtils.userInfo(user) + ", thread: " + Thread.currentThread().getName()); + return null; + } + loggedSessions.putIfAbsent(user.getId(), new CopyOnWriteArrayList<>()); + if(!loggedSessions.get(user.getId()).contains(session)) { + loggedSessions.get(user.getId()).add(session); + return loggedUsers.put(user.getId(), user); + } + LOG.warn("session exists for: " + LoggingUtils.userInfo(user) + ", thread: " + Thread.currentThread().getName()); + return null; + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void updateUser(User user) { + lock.writeLock().lock(); + try { + update(user, loggedUsers, loggedSessions, blocked); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void updateUsers(UsersProfileVO profile) { + lock.writeLock().lock(); + try { + for(User user: new ArrayList<>(loggedUsers.values())) { + if(user.getUserProfile() == profile.getId()) { + profile.apply(user); + update(user, loggedUsers, loggedSessions, blocked); + } + } + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public User removeUser(User user, HttpSession session) { + lock.writeLock().lock(); + try { + if("setAttribute".equals(blocked.get())) { + LOG.warn("blocked removeUser: " + LoggingUtils.userInfo(user) + ", thread: " + Thread.currentThread().getName()); + return null; + } + if (loggedSessions.get(user.getId()) == null || (loggedSessions.get(user.getId()).remove(session) + && loggedSessions.get(user.getId()).isEmpty())) { + loggedSessions.remove(user.getId()); + return loggedUsers.remove(user.getId()); + } + return null; + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public Set getUserIds() { + lock.readLock().lock(); + try { + return loggedUsers.keySet(); + } finally { + lock.readLock().unlock(); + } + } + + private static void update(User user, Map loggedUsers, + Map> loggedSessions, + ThreadLocal blocked) { + User loggedUser = loggedUsers.get(user.getId()); + if(loggedUser == null) { + LOG.warn("not logged user: " + LoggingUtils.userInfo(user) + ", thread: " + Thread.currentThread().getName()); + return; + } + List roles = loggedUser.getAttribute("roles"); + if(roles != null) + user.setAttribute("roles", roles); + loggedSessions.putIfAbsent(user.getId(), new CopyOnWriteArrayList<>()); + for(HttpSession session : loggedSessions.get(user.getId())) { + blocked.set("setAttribute"); + session.setAttribute(SESSION_USER, user); + blocked.set(""); + } + loggedUsers.put(user.getId(), user); + } +} diff --git a/src/org/scada_lts/mango/adapter/MangoEvent.java b/src/org/scada_lts/mango/adapter/MangoEvent.java index 2cf994a07e..cab5948e6e 100644 --- a/src/org/scada_lts/mango/adapter/MangoEvent.java +++ b/src/org/scada_lts/mango/adapter/MangoEvent.java @@ -66,16 +66,12 @@ public interface MangoEvent { List getEventsForDataPoint(int dataPointId, int userId); List getPendingEventsForDataPoint(int dataPointId, int userId); - - List getPendingSimpleEventsForDataSource(int dataSourceId, int userId); List getPendingEventsForDataSource(int dataSourceId, int userId); List getPendingEventsForPublisher(int publisherId, int userId); List getPendingEvents(int typeId, int typeRef1, int userId); - - List getPendingSimpleEvents(int typeId, int typeRef1, int userId); List getPendingEvents(int userId); @@ -132,6 +128,4 @@ public interface MangoEvent { boolean isXidUnique(String xid, int excludeId); List getPendingEventsAlarmLevelMin(int userId, int alarmLevelMin, int limit); - - List getPendingEventsAlarmLevelMin(int userId, int alarmLevelMin, int limit, boolean disabledCache); } diff --git a/src/org/scada_lts/mango/service/EventService.java b/src/org/scada_lts/mango/service/EventService.java index 67491f83f5..6509c6a3bc 100644 --- a/src/org/scada_lts/mango/service/EventService.java +++ b/src/org/scada_lts/mango/service/EventService.java @@ -185,20 +185,6 @@ public List getPendingEvents(int typeId, int typeRef1, int userId } - @Deprecated(since = "2.7.5.4") - @Override - public List getPendingSimpleEvents(int typeId, int typeRef1, int userId) { - - List lst; - if (typeRef1 == -1) { - lst = eventDAO.getPendingEvents(typeId, userId); - } else { - lst = eventDAO.getPendingEvents(typeId, typeRef1, userId); - } - return lst; - - } - @Override public List getEventsForDataPoint(int dataPointId, int userId) { int limit = systemSettingsService.getMiscSettings().getEventPendingLimit(); @@ -235,13 +221,6 @@ public List getPendingEventsForDataSource(int dataSourceId, int u return getPendingEvents(EventType.EventSources.DATA_SOURCE, dataSourceId, userId); } - @Deprecated - @Override - public List getPendingSimpleEventsForDataSource(int dataSourceId, int userId) { - return getPendingSimpleEvents(EventType.EventSources.DATA_SOURCE, dataSourceId, userId); - } - - @Override public List getPendingEventsForPublisher(int publisherId, int userId) { return getPendingEvents(EventType.EventSources.PUBLISHER, publisherId, @@ -251,29 +230,24 @@ public List getPendingEventsForPublisher(int publisherId, int use @Override public List getPendingEvents(int userId) { int limit = systemSettingsService.getMiscSettings().getEventPendingLimit(); - return getPendingEventsAlarmLevelMin(userId, -1, limit, false); + return getPendingEventsAlarmLevelMin(userId, -1, limit); } @Override public List getPendingEventsAlarmLevelMin(int userId, int alarmLevelMin, int limit) { - return getPendingEventsAlarmLevelMin(userId, alarmLevelMin, limit, false); - } - - @Override - public List getPendingEventsAlarmLevelMin(int userId, int alarmLevelMin, int limit, boolean forceDisabledCache) { List results = null; try { boolean cacheEnable = systemSettingsService.getMiscSettings().isEventPendingCacheEnabled(); - if (!forceDisabledCache && cacheEnable) { + int fromSystemSettingsLimit = systemSettingsService.getMiscSettings().getEventPendingLimit(); + int calcLimit = limit > -1 && limit <= fromSystemSettingsLimit ? limit : fromSystemSettingsLimit; + if (cacheEnable) { PendingEventsCache.getInstance().startUpdate(); results = PendingEventsCache.getInstance().getPendingEvents(userId).stream() - .sorted(Comparator.comparing(EventInstance::getActiveTimestamp)) + .sorted(Comparator.comparing(EventInstance::getActiveTimestamp).reversed()) .filter(a -> alarmLevelMin < 0 || a.getAlarmLevel() >= alarmLevelMin) + .limit(calcLimit) .collect(Collectors.toList()); } else { - if(!forceDisabledCache) - PendingEventsCache.getInstance().stopUpdate(); - int fromSystemSettingsLimit = systemSettingsService.getMiscSettings().getEventPendingLimit(); - int calcLimit = limit > -1 ? limit : fromSystemSettingsLimit; + PendingEventsCache.getInstance().stopUpdate(); if(alarmLevelMin > 0) { results = eventDAO.getPendingEventsLimitAlarmLevelMin(userId, alarmLevelMin, calcLimit); } else { diff --git a/src/org/scada_lts/mango/service/PendingEventService.java b/src/org/scada_lts/mango/service/PendingEventService.java index 97bd1dbef0..ea9557399e 100644 --- a/src/org/scada_lts/mango/service/PendingEventService.java +++ b/src/org/scada_lts/mango/service/PendingEventService.java @@ -18,18 +18,23 @@ package org.scada_lts.mango.service; +import com.serotonin.mango.rt.event.type.AlarmLevelType; import com.serotonin.mango.rt.event.EventInstance; +import org.scada_lts.login.ILoggedUsers; +import com.serotonin.mango.vo.User; import com.serotonin.mango.vo.UserComment; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.scada_lts.dao.IUserCommentDAO; -import org.scada_lts.dao.IUserDAO; import org.scada_lts.dao.PendingEventsDAO; +import org.scada_lts.service.IHighestAlarmLevelService; +import org.scada_lts.utils.SystemSettingsUtils; import org.scada_lts.web.beans.ApplicationBeans; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; @Service public class PendingEventService { @@ -40,27 +45,47 @@ public class PendingEventService { private final PendingEventsDAO pendingEventsDAO; - private final IUserDAO userDAO; - private final SystemSettingsService systemSettingsService; + private final IHighestAlarmLevelService highestAlarmLevelService; + private final ILoggedUsers loggedUsers; + public PendingEventService() { userCommentDAO = ApplicationBeans.getUserCommentDaoBean(); + highestAlarmLevelService = ApplicationBeans.getHighestAlarmLevelServiceBean(); + loggedUsers = ApplicationBeans.getLoggedUsersBean(); pendingEventsDAO = new PendingEventsDAO(); - userDAO = ApplicationBeans.getUserDaoBean(); systemSettingsService = new SystemSettingsService(); } public Map> getPendingEvents() { - List users = userDAO.getAll(); + Set users = loggedUsers.getUserIds(); Map> comments = getCacheUserComments(userCommentDAO.getEventComments()); int limit = systemSettingsService.getMiscSettings().getEventPendingLimit(); Map> cacheEvents = new ConcurrentHashMap<>(); for (int userId: users) { - List events = new CopyOnWriteArrayList<>(pendingEventsDAO.getPendingEvents(userId, comments, limit)); - cacheEvents.put(userId, events); + Set events = pendingEventsDAO.getPendingEvents(userId, comments, AlarmLevelType.NONE, + SystemSettingsUtils.getEventPendingUpdateLimit(), 0).stream() + .map(EventInstanceEqualsById::new) + .collect(Collectors.toSet()); + if(!events.isEmpty()) { + int highestAlarmLevelForUser = highestAlarmLevelService.getAlarmLevel(User.onlyId(userId)); + for (AlarmLevelType alarmLevelType : AlarmLevelType.getAlarmLevelsWithoutNone()) { + if(alarmLevelType.getCode() <= highestAlarmLevelForUser) { + long count = events.stream() + .filter(a -> a.getEventInstance().getAlarmLevel() >= alarmLevelType.getCode()) + .count(); + if (count < limit) { + events.addAll(pendingEventsDAO.getPendingEvents(userId, comments, alarmLevelType, limit, 0).stream() + .map(EventInstanceEqualsById::new) + .collect(Collectors.toSet())); + } + } + } + } + cacheEvents.put(userId, events.stream().map(EventInstanceEqualsById::getEventInstance).collect(Collectors.toList())); } return cacheEvents; } @@ -82,4 +107,28 @@ private Map> getCacheUserComments(List c } return mappedUserCommentForEvent; } + + static class EventInstanceEqualsById { + private final EventInstance eventInstance; + public EventInstanceEqualsById(EventInstance eventInstance) { + this.eventInstance = eventInstance; + } + + public EventInstance getEventInstance() { + return eventInstance; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EventInstanceEqualsById)) return false; + EventInstanceEqualsById byId = (EventInstanceEqualsById) o; + return eventInstance.getId() == byId.eventInstance.getId(); + } + + @Override + public int hashCode() { + return Objects.hash(eventInstance.getId()); + } + } } diff --git a/src/org/scada_lts/utils/SystemSettingsUtils.java b/src/org/scada_lts/utils/SystemSettingsUtils.java index cc0f80d285..dacf29b319 100644 --- a/src/org/scada_lts/utils/SystemSettingsUtils.java +++ b/src/org/scada_lts/utils/SystemSettingsUtils.java @@ -35,6 +35,7 @@ private SystemSettingsUtils() {} public static final String VIEW_FORCE_FULL_SCREEN_MODE = "view.forceFullScreen"; public static final String VIEW_HIDE_SHORTCUT_DISABLE_FULL_SCREEN = "view.hideShortcutDisableFullScreen"; public static final String EVENT_PENDING_LIMIT = "event.pending.limit"; + public static final String EVENT_PENDING_UPDATE_LIMIT = "event.pending.update.limit"; public static final String EVENT_PENDING_CACHE_ENABLED = "abilit.cacheEnable"; private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(SystemSettingsUtils.class); @@ -215,13 +216,23 @@ public static int getEventPendingLimit() { } } + public static int getEventPendingUpdateLimit() { + try { + String eventPendingLimit = ScadaConfig.getInstance().getConf().getProperty(EVENT_PENDING_UPDATE_LIMIT, "1000"); + return Integer.parseInt(eventPendingLimit); + } catch (Exception e) { + LOG.error(e.getMessage()); + return 1000; + } + } + public static boolean isEventPendingCacheEnabled() { try { - String eventPendingCache = ScadaConfig.getInstance().getConf().getProperty(EVENT_PENDING_CACHE_ENABLED, "false"); + String eventPendingCache = ScadaConfig.getInstance().getConf().getProperty(EVENT_PENDING_CACHE_ENABLED, "true"); return Boolean.parseBoolean(eventPendingCache); } catch (Exception e) { LOG.error(e.getMessage()); - return false; + return true; } } } diff --git a/src/org/scada_lts/web/beans/ApplicationBeans.java b/src/org/scada_lts/web/beans/ApplicationBeans.java index c9de7e88a2..4507c8e373 100644 --- a/src/org/scada_lts/web/beans/ApplicationBeans.java +++ b/src/org/scada_lts/web/beans/ApplicationBeans.java @@ -5,6 +5,8 @@ import br.org.scadabr.vo.usersProfiles.UsersProfileVO; import com.serotonin.mango.Common; import com.serotonin.mango.view.View; +import org.scada_lts.login.ILoggedUsers; +import org.scada_lts.login.LoggedUsers; import com.serotonin.mango.vo.User; import com.serotonin.mango.vo.WatchList; import com.serotonin.mango.vo.permission.DataPointAccess; @@ -147,6 +149,10 @@ public static EventsServiceWebSocket getEventsServiceWebSocketBean() { return getBeanFromContext("eventsServiceWebSocket", EventsServiceWebSocket.class); } + public static ILoggedUsers getLoggedUsersBean() { + return getBeanFromContext("loggedUsers", LoggedUsers.class); + } + @Deprecated public static class Lazy { diff --git a/test/com/serotonin/mango/vo/LoggedUserTestsSuite.java b/test/com/serotonin/mango/vo/LoggedUserTestsSuite.java new file mode 100644 index 0000000000..2d34e47567 --- /dev/null +++ b/test/com/serotonin/mango/vo/LoggedUserTestsSuite.java @@ -0,0 +1,17 @@ +package com.serotonin.mango.vo; + +import com.serotonin.mango.rt.scripting.ScriptTest; +import com.serotonin.mango.rt.scripting.ScriptWithObjectContextEnableDisableDataPointTest; +import com.serotonin.mango.rt.scripting.ScriptWithObjectContextEnableDisableDataSourceTest; +import com.serotonin.mango.rt.scripting.ScriptWithObjectContextWriteDataPointTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + LoggedUsersTest.class, + LoggedUsersMultiThreadTest.class +}) +public class LoggedUserTestsSuite { +} \ No newline at end of file diff --git a/test/com/serotonin/mango/vo/LoggedUsersMultiThreadTest.java b/test/com/serotonin/mango/vo/LoggedUsersMultiThreadTest.java new file mode 100644 index 0000000000..e9a5be5047 --- /dev/null +++ b/test/com/serotonin/mango/vo/LoggedUsersMultiThreadTest.java @@ -0,0 +1,532 @@ +package com.serotonin.mango.vo; + +import br.org.scadabr.db.utils.TestUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.scada_lts.login.ILoggedUsers; +import org.scada_lts.login.LoggedUsers; +import org.scada_lts.web.beans.ApplicationBeans; +import org.springframework.mock.web.MockHttpSession; +import utils.TestConcurrentUtils; + +import javax.servlet.http.HttpSession; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +import static com.serotonin.mango.Common.SESSION_USER; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ApplicationBeans.class}) +// resources/org/powermock/extensions/configuration.properties is not working +@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*", "com.sun.org.apache.xalan.*", + "javax.activation.*", "javax.management.*"}) +public class LoggedUsersMultiThreadTest { + + private final ILoggedUsers loggedUsers = new LoggedUsers(); + + @Before + public void config() { + mockStatic(ApplicationBeans.class); + when(ApplicationBeans.getLoggedUsersBean()).thenReturn(loggedUsers); + } + + @Test + public void when_addUser_for_one_user_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, httpSession1) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_one_removeUser_for_one_user_and_same_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_one_removeUser_for_one_user_and_other_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, new MockHttpSession()) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_one_removeUser_for_one_user_and_two_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_one_user_and_two_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_one_user_and_one_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, new MockHttpSession()) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_one_user_and_other_session_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, user), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_one_user_and_other_session_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, httpSession1), + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, user), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_same_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user2, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_addUser_for_two_user_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_four_addUser_for_two_user_then_logged_two_user() { + //given: + User user = TestUtils.newUser(1); + User user2 = TestUtils.newUser(2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, new MockHttpSession()) + )); + + //then: + Assert.assertEquals(2, loggedUsers.getUserIds().size()); + } + + @Test + public void when_six_addUser_for_two_user_then_logged_two_user() { + //given: + User user = TestUtils.newUser(1); + User user2 = TestUtils.newUser(2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, new MockHttpSession()), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, new MockHttpSession()) + )); + + //then: + Assert.assertEquals(2, loggedUsers.getUserIds().size()); + } + + @Test + public void when_two_removeUser_for_two_user_and_other_session_as_addUser_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, new MockHttpSession()) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_two_session_as_addUser_then_logged_one_user() { + + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_three_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession2), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user2, httpSession3) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_two_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_one_session_as_addUser_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_one_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + TestConcurrentUtils.biConsumer(10, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user2, httpSession3) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_one_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user2, new MockHttpSession()) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_two_user_and_other_session_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user, httpSession1), + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, user), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::removeUser, user2, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_two_user_and_other_session_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user, httpSession1), + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, user), + new TestConcurrentUtils.BiFunctionAction<>(loggedUsers::addUser, user2, httpSession2) + )); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_one_user_and_one_session_then_set_user_in_session() { + //given: + User user = TestUtils.newUser(123); + user.setFirstName("nonupdated"); + User updatedUser = TestUtils.newUser(123); + updatedUser.setFirstName("updated"); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, updatedUser) + )); + + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + + //then: + Assert.assertEquals(updatedUser.isAdmin(), result1.isAdmin()); + } + + @Test + public void when_updateUser_for_one_user_and_two_session_then_set_updated_user_in_two_session() { + //given: + User user = TestUtils.newUser(123); + user.setFirstName("nonupdated"); + User updatedUser = TestUtils.newUser(123); + updatedUser.setFirstName("updated"); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, updatedUser) + )); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + User result2 = (User) httpSession2.getAttribute(SESSION_USER); + + //then: + Assert.assertEquals(updatedUser.getFirstName(), result1.getFirstName()); + Assert.assertEquals(updatedUser.getFirstName(), result2.getFirstName()); + } + + @Test + public void when_updateUser_for_one_user_and_two_session_then_no_set_no_updated_user_in_two_session() { + //given: + User user = TestUtils.newUser(123); + user.setFirstName("nonupdated"); + User updatedUser = TestUtils.newUser(123); + updatedUser.setFirstName("updated"); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + TestConcurrentUtils.biConsumer(1, Arrays.asList( + new TestConcurrentUtils.ConsumerAction<>(loggedUsers::updateUser, updatedUser) + )); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + User result2 = (User) httpSession2.getAttribute(SESSION_USER); + + //then: + Assert.assertNotEquals(user.getFirstName(), result1.getFirstName()); + Assert.assertNotEquals(user.getFirstName(), result2.getFirstName()); + } +} \ No newline at end of file diff --git a/test/com/serotonin/mango/vo/LoggedUsersTest.java b/test/com/serotonin/mango/vo/LoggedUsersTest.java new file mode 100644 index 0000000000..b44bc49113 --- /dev/null +++ b/test/com/serotonin/mango/vo/LoggedUsersTest.java @@ -0,0 +1,535 @@ +package com.serotonin.mango.vo; + +import br.org.scadabr.db.utils.TestUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.scada_lts.login.ILoggedUsers; +import org.scada_lts.login.LoggedUsers; +import org.scada_lts.web.beans.ApplicationBeans; +import org.springframework.mock.web.MockHttpSession; +import utils.TestConcurrentUtils; + +import javax.servlet.http.HttpSession; +import java.util.*; + +import static com.serotonin.mango.Common.SESSION_USER; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ApplicationBeans.class}) +// resources/org/powermock/extensions/configuration.properties is not working +@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*", "com.sun.org.apache.xalan.*", + "javax.activation.*", "javax.management.*"}) +public class LoggedUsersTest { + + private final ILoggedUsers loggedUsers = new LoggedUsers(); + + @Before + public void config() { + mockStatic(ApplicationBeans.class); + when(ApplicationBeans.getLoggedUsersBean()).thenReturn(loggedUsers); + } + + @Test + public void when_addUser_for_one_user_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + + //when: + loggedUsers.addUser(user, httpSession1); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_one_removeUser_for_one_user_and_same_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + loggedUsers.removeUser(user, httpSession1); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_one_removeUser_for_one_user_and_other_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + loggedUsers.removeUser(user, new MockHttpSession()); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_one_removeUser_for_one_user_and_two_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_one_user_and_two_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_one_user_and_one_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user, new MockHttpSession()); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_one_user_and_other_session_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.updateUser(user); + loggedUsers.removeUser(user, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_one_user_and_other_session_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + + //when: + loggedUsers.addUser(user, httpSession1); + loggedUsers.updateUser(user); + loggedUsers.addUser(user, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_same_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user2, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_addUser_for_two_user_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + + //when: + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_other_session_as_addUser_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + loggedUsers.removeUser(user, new MockHttpSession()); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_two_session_as_addUser_then_logged_one_user() { + + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_three_session_as_addUser_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user, httpSession2); + loggedUsers.removeUser(user2, httpSession3); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_two_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_one_session_as_addUser_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + loggedUsers.removeUser(user, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_three_removeUser_for_two_user_and_one_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + HttpSession httpSession3 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + loggedUsers.addUser(user2, httpSession3); + + //when: + loggedUsers.removeUser(user2, httpSession3); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_two_removeUser_for_two_user_and_one_session_as_addUser_then_logged_one_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user2, new MockHttpSession()); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_and_two_removeUser_for_two_user_then_logged_any_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Collections.emptySet(); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user2, httpSession2); + + //when: + loggedUsers.updateUser(user); + loggedUsers.removeUser(user, httpSession1); + loggedUsers.removeUser(user2, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_addUser_and_updateUser_and_addUser_for_two_user_then_logged_two_user() { + //given: + User user = TestUtils.newUser(123); + User user2 = TestUtils.newUser(345); + Set expected = Set.of(user.getId(), user2.getId()); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + + //when: + loggedUsers.addUser(user, httpSession1); + loggedUsers.updateUser(user); + loggedUsers.addUser(user2, httpSession2); + + //then: + Assert.assertEquals(expected, loggedUsers.getUserIds()); + } + + @Test + public void when_updateUser_for_updated_user_and_two_session_then_set_updated_user_in_two_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + User updatedUser = TestUtils.newUser(123); + updatedUser.setAdmin(true); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.updateUser(updatedUser); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + User result2 = (User) httpSession2.getAttribute(SESSION_USER); + + //then: + Assert.assertEquals(updatedUser.isAdmin(), result1.isAdmin()); + Assert.assertEquals(updatedUser.isAdmin(), result1.isAdmin()); + } + + @Test + public void when_updateUser_for_two_updated_user_and_two_session_then_set_second_updated_user_in_two_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + user.setFirstName(""); + User firstUpdatedUser = TestUtils.newUser(123); + firstUpdatedUser.setAdmin(true); + User secondUpdatedUser = TestUtils.newUser(123); + secondUpdatedUser.setFirstName("firstName"); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.updateUser(firstUpdatedUser); + loggedUsers.updateUser(secondUpdatedUser); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + User result2 = (User) httpSession2.getAttribute(SESSION_USER); + + //then: + Assert.assertEquals(secondUpdatedUser.getFirstName(), result1.getFirstName()); + Assert.assertEquals(secondUpdatedUser.getFirstName(), result2.getFirstName()); + } + + @Test + public void when_updateUser_for_updated_user_then_set_updated_user_in_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + User updatedUser = TestUtils.newUser(123); + updatedUser.setAdmin(true); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + loggedUsers.updateUser(updatedUser); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + + //then: + Assert.assertEquals(updatedUser.isAdmin(), result1.isAdmin()); + } + + @Test + public void when_updateUser_for_updated_user_then_not_set_nonupdated_user_in_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + User updatedUser = TestUtils.newUser(123); + updatedUser.setAdmin(true); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + loggedUsers.updateUser(updatedUser); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + + //then: + Assert.assertNotEquals(user.isAdmin(), result1.isAdmin()); + } + + @Test + public void when_updateUser_for_two_updated_user_then_set_second_updated_user_in_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + user.setFirstName(""); + User fisrtUpdatedUser = TestUtils.newUser(123); + fisrtUpdatedUser.setAdmin(true); + User secondUpdatedUser = TestUtils.newUser(123); + secondUpdatedUser.setFirstName("firstName"); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + loggedUsers.updateUser(fisrtUpdatedUser); + loggedUsers.updateUser(secondUpdatedUser); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + + //then: + Assert.assertEquals(secondUpdatedUser.getFirstName(), result1.getFirstName()); + } + + @Test + public void when_updateUser_for_two_updated_user_then_set_not_first_updated_user_in_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + user.setFirstName(""); + User firstUpdatedUser = TestUtils.newUser(123); + firstUpdatedUser.setAdmin(true); + User secondUpdatedUser = TestUtils.newUser(123); + secondUpdatedUser.setFirstName("firstName"); + HttpSession httpSession1 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + + //when: + loggedUsers.updateUser(firstUpdatedUser); + loggedUsers.updateUser(secondUpdatedUser); + User result = (User) httpSession1.getAttribute(SESSION_USER); + + //then: + Assert.assertNotEquals(firstUpdatedUser.getFirstName(), result.getFirstName()); + } + + @Test + public void when_updateUser_for_updated_user_then_not_set_nonupdated_user_in_two_session() { + //given: + User user = TestUtils.newUser(123); + user.setAdmin(false); + User updatedUser = TestUtils.newUser(123); + updatedUser.setAdmin(true); + HttpSession httpSession1 = new MockHttpSession(); + HttpSession httpSession2 = new MockHttpSession(); + loggedUsers.addUser(user, httpSession1); + loggedUsers.addUser(user, httpSession2); + + //when: + loggedUsers.updateUser(updatedUser); + User result1 = (User) httpSession1.getAttribute(SESSION_USER); + User result2 = (User) httpSession2.getAttribute(SESSION_USER); + + //then: + Assert.assertNotEquals(user.isAdmin(), result1.isAdmin()); + Assert.assertNotEquals(user.isAdmin(), result2.isAdmin()); + } +} \ No newline at end of file diff --git a/test/org/scada_lts/ds/messaging/channel/MessagingChannelsMultiThreadTest.java b/test/org/scada_lts/ds/messaging/channel/MessagingChannelsMultiThreadTest.java index 07f0cca158..e5bd0842d9 100644 --- a/test/org/scada_lts/ds/messaging/channel/MessagingChannelsMultiThreadTest.java +++ b/test/org/scada_lts/ds/messaging/channel/MessagingChannelsMultiThreadTest.java @@ -220,16 +220,16 @@ public void when_removeChannel_and_initChannel_on_other_dataPoints_then_one_size //given: messagingChannels.initChannel(dataPointRT2, () -> messagingChannelOpenned1); - TestConcurrentUtils.Action action1 = - new TestConcurrentUtils.Action<>((dp, timeout) -> { + TestConcurrentUtils.BiConsumerAction action1 = + new TestConcurrentUtils.BiConsumerAction<>((dp, timeout) -> { try { messagingChannels.initChannel(dp, () -> messagingChannelOpenned1); } catch (Exception e) { throw new RuntimeException(e); } }, dataPointRT1, 1000); - TestConcurrentUtils.Action action2 = - new TestConcurrentUtils.Action<>((dp, timeout) -> { + TestConcurrentUtils.BiConsumerAction action2 = + new TestConcurrentUtils.BiConsumerAction<>((dp, timeout) -> { try { messagingChannels.removeChannel(dp); } catch (Exception e) { @@ -250,16 +250,16 @@ public void when_removeChannel_and_initChannel_on_other_dataPoints_then_one_isOp //given: messagingChannels.initChannel(dataPointRT2, () -> messagingChannelOpenned1); - TestConcurrentUtils.Action action1 = - new TestConcurrentUtils.Action<>((dp, timeout) -> { + TestConcurrentUtils.BiConsumerAction action1 = + new TestConcurrentUtils.BiConsumerAction<>((dp, timeout) -> { try { messagingChannels.initChannel(dp, () -> messagingChannelOpenned1); } catch (Exception e) { throw new RuntimeException(e); } }, dataPointRT1, 1000); - TestConcurrentUtils.Action action2 = - new TestConcurrentUtils.Action<>((dp, timeout) -> { + TestConcurrentUtils.BiConsumerAction action2 = + new TestConcurrentUtils.BiConsumerAction<>((dp, timeout) -> { try { messagingChannels.removeChannel(dp); } catch (Exception e) { @@ -301,24 +301,24 @@ public void when_isOpenChannel_without_initChannel_then_false() { public void when_initChannel_with_three_dataPoints_then_size_three() throws Exception { //given: - TestConcurrentUtils.Action> action1 = - new TestConcurrentUtils.Action<>((a, b) -> { + TestConcurrentUtils.BiConsumerAction> action1 = + new TestConcurrentUtils.BiConsumerAction<>((a, b) -> { try { messagingChannels.initChannel(a, b); } catch (Exception e) { e.printStackTrace(); } }, dataPointRT1, () -> messagingChannelOpenned1); - TestConcurrentUtils.Action> action2 = - new TestConcurrentUtils.Action<>((a, b) -> { + TestConcurrentUtils.BiConsumerAction> action2 = + new TestConcurrentUtils.BiConsumerAction<>((a, b) -> { try { messagingChannels.initChannel(a, b); } catch (Exception e) { e.printStackTrace(); } }, dataPointRT2, () -> messagingChannelOpenned2); - TestConcurrentUtils.Action> action3 = - new TestConcurrentUtils.Action<>((a, b) -> { + TestConcurrentUtils.BiConsumerAction> action3 = + new TestConcurrentUtils.BiConsumerAction<>((a, b) -> { try { messagingChannels.initChannel(a, b); } catch (Exception e) { diff --git a/test/utils/MultiThreadEngine.java b/test/utils/MultiThreadEngine.java index e47c5a5323..784d9b2b8a 100644 --- a/test/utils/MultiThreadEngine.java +++ b/test/utils/MultiThreadEngine.java @@ -11,7 +11,7 @@ public class MultiThreadEngine { - private static Logger logger = LoggerFactory.getLogger(MultiThreadEngine.class); + private static Logger LOG = LoggerFactory.getLogger(MultiThreadEngine.class); public static void execute(final Executor executor, int concurrency, final Runnable action) { final CountDownLatch ready = new CountDownLatch(concurrency); @@ -24,7 +24,7 @@ public static void execute(final Executor executor, int concurrency, final Runna start.await(); action.run(); } catch (InterruptedException ex) { - logger.error(ex.getMessage(), ex); + LOG.error(ex.getMessage(), ex); } finally { done.countDown(); } @@ -35,9 +35,9 @@ public static void execute(final Executor executor, int concurrency, final Runna long startNanos = System.nanoTime(); start.countDown(); done.await(); - logger.info("time: {}", (System.nanoTime() - startNanos)/1000000000.0); + LOG.info("time: {}", (System.nanoTime() - startNanos)/1000000000.0); } catch (Exception ex) { - logger.error(ex.getMessage(), ex); + LOG.error(ex.getMessage(), ex); } } @@ -54,7 +54,7 @@ public static List execute(final Executor executor, int concurrency, fina R result = action.call(); results.add(result); } catch (Exception ex) { - logger.error(ex.getMessage(), ex); + LOG.error(ex.getMessage(), ex); } finally { done.countDown(); } @@ -65,9 +65,9 @@ public static List execute(final Executor executor, int concurrency, fina long startNanos = System.nanoTime(); start.countDown(); done.await(); - logger.info("time: {}", (System.nanoTime() - startNanos) / 1000000000.0); + LOG.info("time: {}", (System.nanoTime() - startNanos) / 1000000000.0); } catch (Exception ex) { - logger.error(ex.getMessage(), ex); + LOG.error(ex.getMessage(), ex); } return results; } @@ -84,7 +84,7 @@ public static void execute(final Executor executor, int concurrency, final List< start.await(); runnable.run(); } catch (InterruptedException ex) { - logger.error(ex.getMessage(), ex); + LOG.error(ex.getMessage(), ex); } finally { done.countDown(); } @@ -96,9 +96,9 @@ public static void execute(final Executor executor, int concurrency, final List< long startNanos = System.nanoTime(); start.countDown(); done.await(); - logger.info("time: {}", (System.nanoTime() - startNanos)/1000000000.0); + LOG.info("time: {}", (System.nanoTime() - startNanos)/1000000000.0); } catch (Exception ex) { - logger.error(ex.getMessage(), ex); + LOG.error(ex.getMessage(), ex); } } } \ No newline at end of file diff --git a/test/utils/TestConcurrentUtils.java b/test/utils/TestConcurrentUtils.java index 9bc8ef321d..d315839db1 100644 --- a/test/utils/TestConcurrentUtils.java +++ b/test/utils/TestConcurrentUtils.java @@ -32,10 +32,10 @@ public static void biConsumer(int numberOfLaunches, BiConsumer fun, executor.shutdownNow(); } - public static void biConsumer(int numberOfLaunches, List> actions) { + public static void biConsumer(int numberOfLaunches, List actions) { ExecutorService executor = Executors.newFixedThreadPool(numberOfLaunches * actions.size()); List runnables = new ArrayList<>(); - for(Action action: actions) { + for(SupplierVoid action: actions) { runnables.add(action::execute); } MultiThreadEngine.execute(executor, numberOfLaunches, runnables); @@ -92,19 +92,64 @@ public interface SupplierVoid { void execute(); } - public static class Action { - private BiConsumer fun; + public static class BiConsumerAction implements SupplierVoid { + private final BiConsumer fun; A keyA; B keyB; - public Action(BiConsumer fun, A keyA, B keyB) { + public BiConsumerAction(BiConsumer fun, A keyA, B keyB) { this.fun = fun; this.keyA = keyA; this.keyB = keyB; } + @Override public void execute() { fun.accept(keyA, keyB); } } + + public static class ConsumerAction implements SupplierVoid { + private final Consumer fun; + A keyA; + + public ConsumerAction(Consumer fun, A keyA) { + this.fun = fun; + this.keyA = keyA; + } + @Override + public void execute() { + fun.accept(keyA); + } + } + + public static class FunctionAction implements SupplierVoid { + private final Function fun; + A keyA; + + public FunctionAction(Function fun, A keyA) { + this.fun = fun; + this.keyA = keyA; + } + @Override + public void execute() { + fun.apply(keyA); + } + } + + public static class BiFunctionAction implements SupplierVoid { + private final BiFunction fun; + A keyA; + B keyB; + + public BiFunctionAction(BiFunction fun, A keyA, B keyB) { + this.fun = fun; + this.keyA = keyA; + this.keyB = keyB; + } + @Override + public void execute() { + fun.apply(keyA, keyB); + } + } } \ No newline at end of file diff --git a/test/utils/mock/EventServiceMock.java b/test/utils/mock/EventServiceMock.java index 246df5fa19..2b28d870ef 100644 --- a/test/utils/mock/EventServiceMock.java +++ b/test/utils/mock/EventServiceMock.java @@ -91,11 +91,6 @@ public List getPendingEvents(int typeId, int typeRef1, int userId return Collections.emptyList(); } - @Override - public List getPendingSimpleEvents(int typeId, int typeRef1, int userId) { - return Collections.emptyList(); - } - @Override public List getEventsForDataPoint(int dataPointId, int userId) { return Collections.emptyList(); @@ -111,11 +106,6 @@ public List getPendingEventsForDataSource(int dataSourceId, int u return Collections.emptyList(); } - @Override - public List getPendingSimpleEventsForDataSource(int dataSourceId, int userId) { - return Collections.emptyList(); - } - @Override public List getPendingEventsForPublisher(int publisherId, int userId) { return Collections.emptyList(); diff --git a/webapp-resources/env.properties b/webapp-resources/env.properties index 2c2c4b9b82..4a07896f29 100644 --- a/webapp-resources/env.properties +++ b/webapp-resources/env.properties @@ -57,7 +57,7 @@ abilit.disableDataSourcesOnServerStart=false abilit.api.replace.alert.onview=true -abilit.cacheEnable=false +abilit.cacheEnable=true abilit.START_UPDATE_UNSILENCED_ALARM_LEVEL=100000 abilit.START_UPDATE_EVENT_DETECTORS=100000 abilit.START_UPDATE_PENDING_EVENTS=100000 @@ -132,4 +132,5 @@ view.forceFullScreen=false view.hideShortcutDisableFullScreen=false eventdetector.cache.enabled=true event.pending.limit=101 +event.pending.update.limit=5001 thread.name.additional.length=255 \ No newline at end of file From 36aa86da21138379bb5159be5860cb18fdb27ffe Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Fri, 15 Sep 2023 19:51:19 +0200 Subject: [PATCH 20/43] #2694 Work-items API extension - Added endpoints: /history/priority/{priority}/, /history/priority/{priority}/group-by-classes/, /history/priority/{priority}/group-by-classes/count/, /history/process/, /history/process/group-by-classes/, /history/process/group-by-classes/count/ - Added fields in AbstractBeforeAfterWorkItem: createdDate, executedDate - Simplifying the structure WorkItemInfo - Update env.properties for tests - Added test for ThreadUtils.reduceName: ThreadUtilsTest --- build.gradle | 1 + doc/WorkItemInfoAPI.yaml | 75 +++++++++++++++++++ .../work/AbstractBeforeAfterWorkItem.java | 43 +++++++++++ .../mango/rt/maint/work/WorkItemMetrics.java | 4 + .../scada_lts/utils/SystemSettingsUtils.java | 45 ++++++++++- src/org/scada_lts/utils/WorkItemsUtils.java | 24 ++++++ .../web/mvc/api/WorkItemInfoAPI.java | 41 +++++++++- .../web/mvc/api/WorkItemInfoApiService.java | 56 +++++++++++++- .../web/mvc/api/json/WorkItemInfo.java | 18 ++++- test/env.properties | 10 ++- test/org/scada_lts/utils/ThreadUtilsTest.java | 55 ++++++++++++++ webapp-resources/env.properties | 4 + 12 files changed, 368 insertions(+), 8 deletions(-) create mode 100644 test/org/scada_lts/utils/ThreadUtilsTest.java diff --git a/build.gradle b/build.gradle index 6921d48ecb..a78c848bca 100644 --- a/build.gradle +++ b/build.gradle @@ -198,5 +198,6 @@ test { includeTestsMatching "com.serotonin.mango.view.export.CsvWriterTest" includeTestsMatching "com.serotonin.mango.util.EmailValidatorTest" includeTestsMatching "com.serotonin.mango.vo.LoggedUserTestsSuite" + includeTestsMatching "org.scada_lts.utils.ThreadUtilsTest" } } \ No newline at end of file diff --git a/doc/WorkItemInfoAPI.yaml b/doc/WorkItemInfoAPI.yaml index 5e284e1734..bf63122452 100644 --- a/doc/WorkItemInfoAPI.yaml +++ b/doc/WorkItemInfoAPI.yaml @@ -266,6 +266,81 @@ paths: required: true schema: type: 'integer' + /history/priority/{priority}/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /history/priority/{priority}/group-by-classes/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority and group by classes' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /history/priority/{priority}/group-by-classes/count/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority and group by classes then count' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /history/process/: + get: + tags: + - WorkItemInfoAPI + description: 'Get process work items' + responses: + '200': + description: "Get successful" + headers: { } + /history/process/group-by-classes/: + get: + tags: + - WorkItemInfoAPI + description: 'Get process work items and group by classes' + responses: + '200': + description: "Get successful" + headers: { } + /history/process/group-by-classes/count/: + get: + tags: + - WorkItemInfoAPI + description: 'Get process work items and group by classes then count' + responses: + '200': + description: "Get successful" + headers: { } /work-items/less/{executedMs}/: get: tags: diff --git a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java index 9771d95448..ac28f22ec6 100644 --- a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java +++ b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java @@ -6,6 +6,7 @@ import org.scada_lts.utils.SystemSettingsUtils; import org.scada_lts.utils.ThreadUtils; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @@ -14,6 +15,10 @@ public abstract class AbstractBeforeAfterWorkItem implements WorkItem, BeforeWor private static final Log LOG = LogFactory.getLog(AbstractBeforeAfterWorkItem.class); private static final WorkItems FAILED_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getFailedWorkItemsLimit()); + private static final WorkItems HISTORY_PROCESS_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryProcessWorkItemsLimit()); + private static final WorkItems HISTORY_HIGH_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryHighPriorityWorkItemsLimit()); + private static final WorkItems HISTORY_MEDIUM_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryMediumPriorityWorkItemsLimit()); + private static final WorkItems HISTORY_LOW_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryLowPriorityWorkItemsLimit()); private static final WorkItems RUNNING_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getRunningWorkItemsLimit(), SystemSettingsUtils.getRepeatRunningWorkItems()); private static final WorkItems EXECUTED_LONGER_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryExecutedLongerWorkItemsLimit()); private static final int EXECUTED_LONGER_WORK_ITEMS_THAN = SystemSettingsUtils.getHistoryExecutedLongerWorkItemsThan(); @@ -25,6 +30,8 @@ public abstract class AbstractBeforeAfterWorkItem implements WorkItem, BeforeWor private volatile String threadName = ""; private volatile String failedMessage = ""; private volatile String workFailedMessage = ""; + private volatile LocalDateTime executedDate = null; + private final LocalDateTime createdDate = LocalDateTime.now(); private final String suffixThreadName = ThreadUtils.reduceName(" - " + WorkItemPriority.priorityOf(getPriority()) + " - " + this.getDetails()); public static WorkItems failedWorkItems() { @@ -36,12 +43,38 @@ public static WorkItems runningWorkItems() { public static WorkItems executedLongerWorkItems() { return EXECUTED_LONGER_WORK_ITEMS; } + public static WorkItems processWorkItems() { + return HISTORY_PROCESS_WORK_ITEMS; + } + public static WorkItems highPriorityWorkItems() { + return HISTORY_HIGH_PRIORITY_WORK_ITEMS; + } + public static WorkItems mediumPriorityWorkItems() { + return HISTORY_MEDIUM_PRIORITY_WORK_ITEMS; + } + public static WorkItems lowPriorityWorkItems() { + return HISTORY_LOW_PRIORITY_WORK_ITEMS; + } private static void addWorkItemAfterExecuted(WorkItem workItem, boolean failed, int executedMs) { if(failed) FAILED_WORK_ITEMS.add(workItem); if(executedMs > EXECUTED_LONGER_WORK_ITEMS_THAN) EXECUTED_LONGER_WORK_ITEMS.add(workItem); + if(workItem instanceof ProcessWorkItem || workItem instanceof ProcessWorkItem.InputReader + || workItem instanceof ProcessWorkItem.ProcessTimeout) + HISTORY_PROCESS_WORK_ITEMS.add(workItem); + switch (WorkItemPriority.priorityOf(workItem.getPriority())) { + case HIGH: + HISTORY_HIGH_PRIORITY_WORK_ITEMS.add(workItem); + break; + case MEDIUM: + HISTORY_MEDIUM_PRIORITY_WORK_ITEMS.add(workItem); + break; + case LOW: + HISTORY_LOW_PRIORITY_WORK_ITEMS.add(workItem); + break; + } } private static void addWorkItemIfNotRunning(WorkItem workItem, boolean running) { if(running) { @@ -138,6 +171,7 @@ public final void execute() { this.running = false; if(!StringUtils.isEmpty(this.suffixThreadName)) Thread.currentThread().setName(this.threadName); + this.executedDate = LocalDateTime.now(); } } @@ -208,4 +242,13 @@ public String getWorkFailedMessage() { public String getThreadName() { return threadName; } + + @Override + public LocalDateTime getCreatedDate() { + return createdDate; + } + @Override + public LocalDateTime getExecutedDate() { + return executedDate; + } } diff --git a/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java b/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java index 3884452048..65755bea22 100644 --- a/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java +++ b/src/com/serotonin/mango/rt/maint/work/WorkItemMetrics.java @@ -1,5 +1,7 @@ package com.serotonin.mango.rt.maint.work; +import java.time.LocalDateTime; + public interface WorkItemMetrics { boolean isExecuted(); boolean isSuccess(); @@ -9,4 +11,6 @@ public interface WorkItemMetrics { String getFailedMessage(); String getWorkFailedMessage(); String getThreadName(); + LocalDateTime getCreatedDate(); + LocalDateTime getExecutedDate(); } diff --git a/src/org/scada_lts/utils/SystemSettingsUtils.java b/src/org/scada_lts/utils/SystemSettingsUtils.java index dacf29b319..20964a9294 100644 --- a/src/org/scada_lts/utils/SystemSettingsUtils.java +++ b/src/org/scada_lts/utils/SystemSettingsUtils.java @@ -24,7 +24,10 @@ private SystemSettingsUtils() {} private static final String PROCESSING_FAILED_WORK_ITEMS_LIMIT_KEY = "processing.workitems.failed.limit"; private static final String PROCESSING_RUNNING_WORK_ITEMS_LIMIT_KEY = "processing.workitems.running.limit"; private static final String PROCESSING_REPEAT_RUNNING_WORK_ITEMS_KEY = "processing.workitems.running.repeat"; - + private static final String PROCESSING_HISTORY_PROCESS_WORK_ITEMS_LIMIT_KEY = "processing.workitems.history.process.limit"; + private static final String PROCESSING_HISTORY_HIGH_WORK_ITEMS_LIMIT_KEY = "processing.workitems.history.priority.high.limit"; + private static final String PROCESSING_HISTORY_MEDIUM_WORK_ITEMS_LIMIT_KEY = "processing.workitems.history.priority.medium.limit"; + private static final String PROCESSING_HISTORY_LOW_WORK_ITEMS_LIMIT_KEY = "processing.workitems.history.priority.low.limit"; private static final String PROCESSING_HISTORY_EXECUTED_LONGER_WORK_ITEMS_THAN_MS_KEY = "processing.workitems.history.longer.thanMs"; private static final String PROCESSING_HISTORY_EXECUTED_LONGER_WORK_ITEMS_LIMIT_KEY = "processing.workitems.history.longer.limit"; private static final String SECURITY_JS_ACCESS_DENIED_METHOD_REGEXES = "scadalts.security.js.access.denied.method.regexes"; @@ -136,6 +139,46 @@ public static int getRunningWorkItemsLimit() { } } + public static int getHistoryProcessWorkItemsLimit() { + try { + String limit = ScadaConfig.getInstance().getConf().getProperty(PROCESSING_HISTORY_PROCESS_WORK_ITEMS_LIMIT_KEY, "100"); + return Integer.parseInt(limit); + } catch (Exception e) { + LOG.error(e.getMessage()); + return 100; + } + } + + public static int getHistoryHighPriorityWorkItemsLimit() { + try { + String limit = ScadaConfig.getInstance().getConf().getProperty(PROCESSING_HISTORY_HIGH_WORK_ITEMS_LIMIT_KEY, "100"); + return Integer.parseInt(limit); + } catch (Exception e) { + LOG.error(e.getMessage()); + return 100; + } + } + + public static int getHistoryMediumPriorityWorkItemsLimit() { + try { + String limit = ScadaConfig.getInstance().getConf().getProperty(PROCESSING_HISTORY_MEDIUM_WORK_ITEMS_LIMIT_KEY, "100"); + return Integer.parseInt(limit); + } catch (Exception e) { + LOG.error(e.getMessage()); + return 100; + } + } + + public static int getHistoryLowPriorityWorkItemsLimit() { + try { + String limit = ScadaConfig.getInstance().getConf().getProperty(PROCESSING_HISTORY_LOW_WORK_ITEMS_LIMIT_KEY, "100"); + return Integer.parseInt(limit); + } catch (Exception e) { + LOG.error(e.getMessage()); + return 100; + } + } + public static int getRepeatRunningWorkItems() { try { String limit = ScadaConfig.getInstance().getConf().getProperty(PROCESSING_REPEAT_RUNNING_WORK_ITEMS_KEY, "0"); diff --git a/src/org/scada_lts/utils/WorkItemsUtils.java b/src/org/scada_lts/utils/WorkItemsUtils.java index e25d500c42..aeb836f0a0 100644 --- a/src/org/scada_lts/utils/WorkItemsUtils.java +++ b/src/org/scada_lts/utils/WorkItemsUtils.java @@ -90,4 +90,28 @@ public static List getByPriority(WorkItemPriority priority) { .filter(a -> a.getWorkItem().isExecuted() && a.getPriority() == priority) .collect(Collectors.toList()); } + + public static List getHistoryProcess() { + return AbstractBeforeAfterWorkItem.processWorkItems().get().stream() + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + } + + public static List getHistoryHighPriority() { + return AbstractBeforeAfterWorkItem.highPriorityWorkItems().get().stream() + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + } + + public static List getHistoryMediumPriority() { + return AbstractBeforeAfterWorkItem.mediumPriorityWorkItems().get().stream() + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + } + + public static List getHistoryLowPriority() { + return AbstractBeforeAfterWorkItem.lowPriorityWorkItems().get().stream() + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + } } diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java index d086e9502e..3fc72256cf 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java @@ -174,6 +174,45 @@ public ResponseEntity> getExecutedLongerWorkItemsGroupByCountH return new ResponseEntity<>(response, HttpStatus.OK); } + @GetMapping(value = "/history/priority/{priority}") + public ResponseEntity getExecutedWorkPriority(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + List response = workItemInfoApiService.getWorkItemsByPriority(request, priority); + return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); + } + + @GetMapping(value = "/history/priority/{priority}/group-by-classes") + public ResponseEntity>> getExecutedWorkPriorityGroupBy(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + Map> response = workItemInfoApiService.getWorkItemsByPriorityGroupByClassName(request, priority); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/history/priority/{priority}/group-by-classes/count") + public ResponseEntity> getExecutedWorkPriorityGroupByCount(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + Mapresponse = workItemInfoApiService.getWorkItemsByPriorityGroupByClassNameCount(request, priority); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/history/process") + public ResponseEntity getProcessWorkItems(HttpServletRequest request) { + List response = workItemInfoApiService.getProcessWorkItems(request); + return new ResponseEntity<>(new WorkItemInfoList(response), HttpStatus.OK); + } + + @GetMapping(value = "/history/process/group-by-classes") + public ResponseEntity>> getProcessWorkItemsGroupBy(HttpServletRequest request) { + Map> response = workItemInfoApiService.getProcessWorkItemsGroupByClassName(request); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/history/process/group-by-classes/count") + public ResponseEntity> getProcessWorkItemsGroupByCount(HttpServletRequest request) { + Map response = workItemInfoApiService.getProcessWorkItemsGroupByClassNameCount(request); + return new ResponseEntity<>(response, HttpStatus.OK); + } + @GetMapping(value = "/less/{executedMs}") public ResponseEntity getExecutedLessWorkItems(HttpServletRequest request, @PathVariable("executedMs") int executedMs) { @@ -226,7 +265,7 @@ public ScheduledWorkItemInfoAPI(ScheduledWorkItemInfoApiService scheduledWorkIte this.scheduledWorkItemInfoApiService = scheduledWorkItemInfoApiService; } - @GetMapping(value = "/") + @GetMapping(value = "") public ResponseEntity> getScheduledWorkItems(HttpServletRequest request) { List response = scheduledWorkItemInfoApiService.getScheduledWorkItems(request); return new ResponseEntity<>(new ScheduledWorkItemInfoList<>(response), HttpStatus.OK); diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java index 1c1bd2489b..b2251f8507 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -25,6 +26,57 @@ public List getWorkItems(HttpServletRequest request) { return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getAll); } + public List getProcessWorkItems(HttpServletRequest request) { + return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryProcess); + } + + public Map> getProcessWorkItemsGroupByClassName(HttpServletRequest request) { + return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryProcess); + } + + public Map getProcessWorkItemsGroupByClassNameCount(HttpServletRequest request) { + return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryProcess); + } + + public List getWorkItemsByPriority(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryHighPriority); + case MEDIUM: + return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryMediumPriority); + case LOW: + return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryLowPriority); + default: + return Collections.emptyList(); + } + } + + public Map> getWorkItemsByPriorityGroupByClassName(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryHighPriority); + case MEDIUM: + return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryMediumPriority); + case LOW: + return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryLowPriority); + default: + return Collections.emptyMap(); + } + } + + public Map getWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryHighPriority); + case MEDIUM: + return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryMediumPriority); + case LOW: + return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryLowPriority); + default: + return Collections.emptyMap(); + } + } + public List getExecutedWorkItems(HttpServletRequest request) { return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getByExecuted); } @@ -179,14 +231,14 @@ private static Map groupByClassNameCounting(List a.getWorkItemExecute().getClassName(), Collectors.counting())); + .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.counting())); } private static Map> groupByClassName(List workItems, Comparator comparator) { return workItems.stream() .sorted(comparator) .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(a -> a.getWorkItemExecute().getClassName(), Collectors.toList())); + .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.toList())); } private static Map groupByClassNameCounting(List workItems) { diff --git a/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java b/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java index e2fba2c614..e75ce68a6d 100644 --- a/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java +++ b/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java @@ -1,5 +1,7 @@ package org.scada_lts.web.mvc.api.json; +import com.serotonin.mango.rt.maint.work.WorkItem; +import com.serotonin.mango.rt.maint.work.WorkItemPriority; import com.serotonin.mango.rt.maint.work.WorkItems; public class WorkItemInfo { @@ -9,7 +11,19 @@ public WorkItemInfo(WorkItems.Execute workItem) { this.workItem = workItem; } - public WorkItems.Execute getWorkItemExecute() { - return workItem; + public WorkItem getWorkItem() { + return workItem.getWorkItem(); + } + + public long getSerial() { + return workItem.getSerial(); + } + + public String getClassName() { + return workItem.getClassName(); + } + + public WorkItemPriority getPriority() { + return workItem.getPriority(); } } diff --git a/test/env.properties b/test/env.properties index 93f858720d..c924f02079 100644 --- a/test/env.properties +++ b/test/env.properties @@ -57,7 +57,7 @@ abilit.disableDataSourcesOnServerStart=false abilit.api.replace.alert.onview=true -abilit.cacheEnable=false +abilit.cacheEnable=true abilit.START_UPDATE_UNSILENCED_ALARM_LEVEL=100000 abilit.START_UPDATE_EVENT_DETECTORS=100000 abilit.START_UPDATE_PENDING_EVENTS=100000 @@ -120,6 +120,10 @@ processing.workitems.limit=51 processing.workitems.failed.limit=51 processing.workitems.running.limit=51 processing.workitems.running.repeat=51 +processing.workitems.history.process.limit=51 +processing.workitems.history.priority.high.limit=51 +processing.workitems.history.priority.medium.limit=51 +processing.workitems.history.priority.low.limit=51 processing.workitems.history.longer.thanMs=1500 processing.workitems.history.longer.limit=200 @@ -131,4 +135,6 @@ scadalts.security.js.access.granted.class.regexes=^.* view.forceFullScreen=false view.hideShortcutDisableFullScreen=false eventdetector.cache.enabled=true -event.pending.limit=101 \ No newline at end of file +event.pending.limit=101 +event.pending.update.limit=5001 +thread.name.additional.length=5 \ No newline at end of file diff --git a/test/org/scada_lts/utils/ThreadUtilsTest.java b/test/org/scada_lts/utils/ThreadUtilsTest.java new file mode 100644 index 0000000000..4c4ae6b476 --- /dev/null +++ b/test/org/scada_lts/utils/ThreadUtilsTest.java @@ -0,0 +1,55 @@ +package org.scada_lts.utils; + +import org.junit.Assert; +import org.junit.Test; + +public class ThreadUtilsTest { + + @Test + public void when_reduceName_for_abc12345_and_limit_5_then_abc12_() { + //given: + String expected = "abc12.."; + + //when: + String result = ThreadUtils.reduceName("abc12345"); + + //then: + Assert.assertEquals(expected, result); + } + + @Test + public void when_reduceName_for_abc123_and_limit_5_then_abc12_() { + //given: + String expected = "abc12.."; + + //when: + String result = ThreadUtils.reduceName("abc123"); + + //then: + Assert.assertEquals(expected, result); + } + + @Test + public void when_reduceName_for_abc12_and_limit_5_then_abc12() { + //given: + String expected = "abc12"; + + //when: + String result = ThreadUtils.reduceName("abc12"); + + //then: + Assert.assertEquals(expected, result); + } + + @Test + public void when_reduceName_for_abc1_and_limit_5_then_abc1() { + //given: + String expected = "abc1"; + + //when: + String result = ThreadUtils.reduceName("abc1"); + + //then: + Assert.assertEquals(expected, result); + } +} \ No newline at end of file diff --git a/webapp-resources/env.properties b/webapp-resources/env.properties index 4a07896f29..5fd5deb4e2 100644 --- a/webapp-resources/env.properties +++ b/webapp-resources/env.properties @@ -120,6 +120,10 @@ processing.workitems.limit=51 processing.workitems.failed.limit=51 processing.workitems.running.limit=51 processing.workitems.running.repeat=51 +processing.workitems.history.process.limit=51 +processing.workitems.history.priority.high.limit=51 +processing.workitems.history.priority.medium.limit=51 +processing.workitems.history.priority.low.limit=51 processing.workitems.history.longer.thanMs=1500 processing.workitems.history.longer.limit=200 From 66201beaa944a71d85356de4f061dd30079be367 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Fri, 15 Sep 2023 22:08:44 +0200 Subject: [PATCH 21/43] #2694 Work-items API extension - Fixed SendEmailData.toString, EmailAfterWorkItem.toString, MangoEmailContentImpl.getSubject - Added test: CreateWorkItemToStringTest - Removed deprecated method: BatchWriteBehind.add(BatchWriteBehindEntry e, PointValueService pointValueService) - Removed deprecated constructor: BatchWriteBehind(PointValueService pointValueService) - Removed deprecated field: BatchWriteBehind.pointValueService --- build.gradle | 1 + .../rt/maint/work/EmailAfterWorkItem.java | 2 +- .../serotonin/mango/util/SendEmailData.java | 2 +- .../web/email/MangoEmailContentImpl.java | 2 + .../mango/service/PointValueService.java | 17 +----- .../work/CreateWorkItemToStringTest.java | 54 +++++++++++++++++++ 6 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java diff --git a/build.gradle b/build.gradle index a78c848bca..7c0b9af350 100644 --- a/build.gradle +++ b/build.gradle @@ -199,5 +199,6 @@ test { includeTestsMatching "com.serotonin.mango.util.EmailValidatorTest" includeTestsMatching "com.serotonin.mango.vo.LoggedUserTestsSuite" includeTestsMatching "org.scada_lts.utils.ThreadUtilsTest" + includeTestsMatching "com.serotonin.mango.rt.maint.work.CreateWorkItemToStringTest" } } \ No newline at end of file diff --git a/src/com/serotonin/mango/rt/maint/work/EmailAfterWorkItem.java b/src/com/serotonin/mango/rt/maint/work/EmailAfterWorkItem.java index 36f49ff946..666cb94d69 100644 --- a/src/com/serotonin/mango/rt/maint/work/EmailAfterWorkItem.java +++ b/src/com/serotonin/mango/rt/maint/work/EmailAfterWorkItem.java @@ -51,7 +51,7 @@ public String toString() { return "EmailAfterWorkItem{" + "sendEmailData=" + sendEmailData + ", sendEmailConfig=" + sendEmailConfig + - ", details='" + workItemDetails.getDetails() + + ", details='" + (workItemDetails == null ? "null" : workItemDetails.getDetails()) + "'}"; } diff --git a/src/com/serotonin/mango/util/SendEmailData.java b/src/com/serotonin/mango/util/SendEmailData.java index 076dbe4b33..5ca2c94860 100644 --- a/src/com/serotonin/mango/util/SendEmailData.java +++ b/src/com/serotonin/mango/util/SendEmailData.java @@ -34,7 +34,7 @@ public String toString() { return "SendEmailData{" + "fromAddress=" + fromAddress + ", toAddresses=" + Arrays.toString(toAddresses) + - ", subject=" + content.getSubject() + + ", subject=" + (content == null ? "null" : content.getSubject()) + '}'; } } diff --git a/src/com/serotonin/mango/web/email/MangoEmailContentImpl.java b/src/com/serotonin/mango/web/email/MangoEmailContentImpl.java index d93ef54d22..361dbe3963 100644 --- a/src/com/serotonin/mango/web/email/MangoEmailContentImpl.java +++ b/src/com/serotonin/mango/web/email/MangoEmailContentImpl.java @@ -53,6 +53,8 @@ protected MangoEmailContentImpl(IMsgContent emailContent, ResourceBundle bundle, @Override public String getSubject() { + if(subjectDirective == null) + return defaultSubject; String subject = subjectDirective.getSubject(); if (subject == null) return defaultSubject; diff --git a/src/org/scada_lts/mango/service/PointValueService.java b/src/org/scada_lts/mango/service/PointValueService.java index d2928f63da..b6c6852b7a 100644 --- a/src/org/scada_lts/mango/service/PointValueService.java +++ b/src/org/scada_lts/mango/service/PointValueService.java @@ -516,11 +516,6 @@ static class BatchWriteBehind extends AbstractBeforeAfterWorkItem { Common.MONITORED_VALUES.addIfMissingStatMonitor(INSTANCES_MONITOR); } - @Deprecated - static void add(BatchWriteBehindEntry e, PointValueService pointValueService) { - add(e); - } - static void add(BatchWriteBehindEntry e) { synchronized (ENTRIES) { ENTRIES.push(e); @@ -543,17 +538,7 @@ static void add(BatchWriteBehindEntry e) { } } - @Deprecated - private final PointValueService pointValueService; - - public BatchWriteBehind() { - this.pointValueService = null; - } - - @Deprecated - public BatchWriteBehind(PointValueService pointValueService) { - this.pointValueService = pointValueService; - } + public BatchWriteBehind() {} @Override public void work() { diff --git a/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java b/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java new file mode 100644 index 0000000000..68b7420e16 --- /dev/null +++ b/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java @@ -0,0 +1,54 @@ +package com.serotonin.mango.rt.maint.work; + +import org.junit.Test; + +public class CreateWorkItemToStringTest { + + @Test + public void when_EmailFinallyWorkItem_newInstance() { + //when: + WorkItem workItem = EmailFinallyWorkItem.newInstance(null, null, null, null, null); + //then: + System.out.println(workItem); + } + + @Test + public void when_EmailAfterWorkItem_newInstance() { + //when: + WorkItem workItem = EmailAfterWorkItem.newInstance(null, null, null, null); + //then: + System.out.println(workItem); + } + + @Test + public void when_new_ReportWorkItem() { + //when: + WorkItem workItem = new ReportWorkItem(); + //then: + System.out.println(workItem); + } + + @Test + public void when_new_ProcessWorkItem() { + //when: + WorkItem workItem = new ProcessWorkItem(null, null); + //then: + System.out.println(workItem); + } + + @Test + public void when_new_SetPointWorkItem() { + //when: + WorkItem workItem = new SetPointWorkItem(-1, null, null); + //then: + System.out.println(workItem); + } + + @Test + public void when_new_PointLinkSetPointWorkItem() { + //when: + WorkItem workItem = new PointLinkSetPointWorkItem(-1, null, null); + //then: + System.out.println(workItem); + } +} \ No newline at end of file From 86a781e8ae3f5866d58cec19f431328668ac7208 Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Tue, 19 Sep 2023 13:37:41 +0200 Subject: [PATCH 22/43] #2694 Work-items API extension - corrected urls WorkItemInfoAPI.yaml --- doc/WorkItemInfoAPI.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/WorkItemInfoAPI.yaml b/doc/WorkItemInfoAPI.yaml index bf63122452..4bf4682688 100644 --- a/doc/WorkItemInfoAPI.yaml +++ b/doc/WorkItemInfoAPI.yaml @@ -266,7 +266,7 @@ paths: required: true schema: type: 'integer' - /history/priority/{priority}/: + /work-items/history/priority/{priority}/: get: tags: - WorkItemInfoAPI @@ -282,7 +282,7 @@ paths: schema: type: 'string' enum: [ HIGH, MEDIUM, LOW ] - /history/priority/{priority}/group-by-classes/: + /work-items/history/priority/{priority}/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -298,7 +298,7 @@ paths: schema: type: 'string' enum: [ HIGH, MEDIUM, LOW ] - /history/priority/{priority}/group-by-classes/count/: + /work-items/history/priority/{priority}/group-by-classes/count/: get: tags: - WorkItemInfoAPI @@ -314,7 +314,7 @@ paths: schema: type: 'string' enum: [ HIGH, MEDIUM, LOW ] - /history/process/: + /work-items/history/process/: get: tags: - WorkItemInfoAPI @@ -323,7 +323,7 @@ paths: '200': description: "Get successful" headers: { } - /history/process/group-by-classes/: + /work-items/history/process/group-by-classes/: get: tags: - WorkItemInfoAPI @@ -332,7 +332,7 @@ paths: '200': description: "Get successful" headers: { } - /history/process/group-by-classes/count/: + /work-items/history/process/group-by-classes/count/: get: tags: - WorkItemInfoAPI From 4d8d1fc892da21cfabfb220deebadc1c72c32aff Mon Sep 17 00:00:00 2001 From: kamiljarmusik Date: Thu, 21 Sep 2023 01:18:04 +0200 Subject: [PATCH 23/43] #2706 Fixed save Graphical View with Chart Comparator: - For content exceeding 65535 bytes in encode utf-8, using the writeObject method which ultimately uses the writeLongUTF method for String (by calls writeString), unfortunately there is no other way to call this method except through the writeObject method - moved class SerializationHelper from seroUtils lib and modified; - Limiting the list of points to those to which the user has access - in ChartComparatorComponent.createChartComparatorContent use GetDataPointsWithAccess.getObjectIdentifiersWithAccess; - Limiting the ability to display data from points to which the user does not have access - in ViewDwr.getChartData filtering Data Points to which the user does not have access; - Shortening the component loading time by almost 4 times by getting the list of points once, instead of separately for each select from the Chart Comparator component - refactor method ChartComparatorComponent.createDataPointsSelectComponent, extract Data Points List as arguments for this method, and one-time get of point identifiers, instead of full objects; - Added test: SerializationHelperTest - Added test util: DataTestUtils.generateStringUtfRandom --- build.gradle | 1 + .../component/ChartComparatorComponent.java | 26 ++-- src/com/serotonin/mango/web/dwr/ViewDwr.java | 15 +- .../serotonin/util/SerializationHelper.java | 129 ++++++++++++++++++ .../3_5mb_5242880_bytes_iso_latin1.txt | 1 + .../encoding/3_5mb_5242880_bytes_us_ascii.txt | 1 + .../encoding/5mb_5242880_bytes_utf8.txt | 1 + .../encoding/65535_bytes_iso_latin1.txt | 1 + .../encoding/65535_bytes_us_ascii.txt | 1 + test-resources/encoding/65535_bytes_utf16.txt | Bin 0 -> 87792 bytes test-resources/encoding/65535_bytes_utf8.txt | 1 + .../encoding/65536_bytes_iso_latin1.txt | 1 + .../encoding/65536_bytes_us_ascii.txt | 1 + test-resources/encoding/65536_bytes_utf16.txt | Bin 0 -> 87760 bytes test-resources/encoding/65536_bytes_utf8.txt | 1 + .../encoding/662_bytes_iso_latin1.txt | 1 + .../encoding/662_bytes_us_ascii.txt | 1 + test-resources/encoding/662_bytes_utf16.txt | Bin 0 -> 1062 bytes test-resources/encoding/662_bytes_utf8.txt | 1 + .../encoding/7mb_5242880_bytes_utf16.txt | Bin 0 -> 7018428 bytes .../util/SerializationHelperTest.java | 92 +++++++++++++ .../utils/UploadBackgroundFileUtilsTest.java | 4 +- .../utils/UploadGraphicsFileUtilsTest.java | 4 +- test/utils/DataTestUtils.java | 32 +++++ ...dFileTestUtils.java => FileTestUtils.java} | 2 +- 25 files changed, 296 insertions(+), 21 deletions(-) create mode 100644 src/com/serotonin/util/SerializationHelper.java create mode 100644 test-resources/encoding/3_5mb_5242880_bytes_iso_latin1.txt create mode 100644 test-resources/encoding/3_5mb_5242880_bytes_us_ascii.txt create mode 100644 test-resources/encoding/5mb_5242880_bytes_utf8.txt create mode 100644 test-resources/encoding/65535_bytes_iso_latin1.txt create mode 100644 test-resources/encoding/65535_bytes_us_ascii.txt create mode 100644 test-resources/encoding/65535_bytes_utf16.txt create mode 100644 test-resources/encoding/65535_bytes_utf8.txt create mode 100644 test-resources/encoding/65536_bytes_iso_latin1.txt create mode 100644 test-resources/encoding/65536_bytes_us_ascii.txt create mode 100644 test-resources/encoding/65536_bytes_utf16.txt create mode 100644 test-resources/encoding/65536_bytes_utf8.txt create mode 100644 test-resources/encoding/662_bytes_iso_latin1.txt create mode 100644 test-resources/encoding/662_bytes_us_ascii.txt create mode 100644 test-resources/encoding/662_bytes_utf16.txt create mode 100644 test-resources/encoding/662_bytes_utf8.txt create mode 100644 test-resources/encoding/7mb_5242880_bytes_utf16.txt create mode 100644 test/com/serotonin/util/SerializationHelperTest.java create mode 100644 test/utils/DataTestUtils.java rename test/utils/{UploadFileTestUtils.java => FileTestUtils.java} (89%) diff --git a/build.gradle b/build.gradle index 7c0b9af350..01d07a7fd4 100644 --- a/build.gradle +++ b/build.gradle @@ -200,5 +200,6 @@ test { includeTestsMatching "com.serotonin.mango.vo.LoggedUserTestsSuite" includeTestsMatching "org.scada_lts.utils.ThreadUtilsTest" includeTestsMatching "com.serotonin.mango.rt.maint.work.CreateWorkItemToStringTest" + includeTestsMatching "com.serotonin.util.SerializationHelperTest" } } \ No newline at end of file diff --git a/src/br/org/scadabr/view/component/ChartComparatorComponent.java b/src/br/org/scadabr/view/component/ChartComparatorComponent.java index fa68fce774..ec11fbcc88 100644 --- a/src/br/org/scadabr/view/component/ChartComparatorComponent.java +++ b/src/br/org/scadabr/view/component/ChartComparatorComponent.java @@ -12,12 +12,15 @@ import com.serotonin.json.JsonRemoteEntity; import com.serotonin.json.JsonRemoteProperty; -import com.serotonin.mango.db.dao.DataPointDao; +import com.serotonin.mango.Common; import com.serotonin.mango.view.ImplDefinition; import com.serotonin.mango.view.component.HtmlComponent; import com.serotonin.mango.view.component.ViewComponent; -import com.serotonin.mango.vo.DataPointVO; +import com.serotonin.mango.vo.User; import com.serotonin.util.SerializationHelper; +import org.scada_lts.dao.DataPointDAO; +import org.scada_lts.dao.model.ScadaObjectIdentifier; +import org.scada_lts.permissions.service.GetDataPointsWithAccess; @JsonRemoteEntity public class ChartComparatorComponent extends HtmlComponent { @@ -61,11 +64,13 @@ public String createChartComparatorContent() { // sb.append("
"); sb.append("
"); - - sb.append(createDataPointsSelectComponent(idPrefix + "_dp1")); - sb.append(createDataPointsSelectComponent(idPrefix + "_dp2")); - sb.append(createDataPointsSelectComponent(idPrefix + "_dp3")); - sb.append(createDataPointsSelectComponent(idPrefix + "_dp4")); + GetDataPointsWithAccess dataPointsWithAccess = new GetDataPointsWithAccess(new DataPointDAO()); + User user = Common.getUser(); + List dataPoints = dataPointsWithAccess.getObjectIdentifiersWithAccess(user); + sb.append(createDataPointsSelectComponent(idPrefix + "_dp1", dataPoints)); + sb.append(createDataPointsSelectComponent(idPrefix + "_dp2", dataPoints)); + sb.append(createDataPointsSelectComponent(idPrefix + "_dp3", dataPoints)); + sb.append(createDataPointsSelectComponent(idPrefix + "_dp4", dataPoints)); sb.append("
"); sb.append("
"); @@ -99,16 +104,15 @@ public String createChartComparatorContent() { return sb.toString(); } - private String createDataPointsSelectComponent(String idPrefix) { - List dataPoints = new DataPointDao().getDataPoints(null, - false); + private String createDataPointsSelectComponent(String idPrefix, List dataPoints) { + StringBuilder sb = new StringBuilder(); sb.append(""/>" type="number" class="formShort"/> + + + + "/>" type="checkbox" onchange="workItemsReportingEnabledChange()"/> + + + + + + "/>" type="checkbox" onchange="workItemsReportingItemsPerSecondEnabledChange()"/> + + + + + + "/>" type="number" class="formShort"/> + + + + + + "/>" type="number" class="formShort"/> + + diff --git a/WebContent/WEB-INF/spring-security.xml b/WebContent/WEB-INF/spring-security.xml index 92ac6c6db5..a5801b4979 100644 --- a/WebContent/WEB-INF/spring-security.xml +++ b/WebContent/WEB-INF/spring-security.xml @@ -72,7 +72,7 @@ - @@ -88,13 +88,13 @@ - + - @@ -110,7 +110,7 @@ - + diff --git a/build.gradle b/build.gradle index a11e7a3c04..0637e329b1 100644 --- a/build.gradle +++ b/build.gradle @@ -203,5 +203,6 @@ test { includeTestsMatching "org.scada_lts.utils.ThreadUtilsTest" includeTestsMatching "com.serotonin.mango.rt.maint.work.CreateWorkItemToStringTest" includeTestsMatching "com.serotonin.util.SerializationHelperTest" + includeTestsMatching "org.scada_lts.web.mvc.api.json.WorkItemInfoListTest" } } \ No newline at end of file diff --git a/doc/ThreadInfoSecureAPI.yaml b/doc/ThreadInfoSecureAPI.yaml deleted file mode 100644 index 60516b74e7..0000000000 --- a/doc/ThreadInfoSecureAPI.yaml +++ /dev/null @@ -1,224 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://localhost:8080/ScadaBR/api' - description: 'Scada development Server' - variables: {} -info: - version: 2.7.6.1 - title: Scada-LTS API via HTTPS - description: 'Scada-LTS OpenAPI Specification. Description contains only a SMS and Email new feature REST API' - termsOfService: '' - contact: - name: 'Kamil Jarmusik - SoftQ Developer' - email: 'kamil.jarmusik@softq.pl' - license: - name: 'GPL-2.0' -paths: - /threads-secure/: - get: - tags: - - ThreadInfoAPI - description: 'Get all threads' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/metrics/: - get: - tags: - - ThreadInfoAPI - description: 'Get metrics threads' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/stack/: - get: - tags: - - ThreadInfoAPI - description: 'Get all stacks from threads' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/names/: - get: - tags: - - ThreadInfoAPI - description: 'Get all thread names' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/classes/: - get: - tags: - - ThreadInfoAPI - description: 'Get all thread classes' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/classes/: - get: - tags: - - ThreadInfoAPI - description: 'Group threads by thread' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/classes/count/: - get: - tags: - - ThreadInfoAPI - description: 'Group threads count by thread' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/stack/: - get: - tags: - - ThreadInfoAPI - description: 'Group threads by stack' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/stack/classes/: - get: - tags: - - ThreadInfoAPI - description: 'Group threads by stack then classes' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/stack/count/: - get: - tags: - - ThreadInfoAPI - description: 'Group threads by stack then count' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/stack/names/: - get: - tags: - - ThreadInfoAPI - description: 'Group threads by stack then names' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/states/: - get: - tags: - - StateThreadInfoAPI - description: 'Group threads by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/states/classes/: - get: - tags: - - StateThreadInfoAPI - description: 'Group thread classes by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/states/count/: - get: - tags: - - StateThreadInfoAPI - description: 'Group threads count by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/group-by/states/names/: - get: - tags: - - StateThreadInfoAPI - description: 'Group thread namew by state' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/states/: - get: - tags: - - StateThreadInfoAPI - description: 'Get all states' - responses: - '200': - description: 'Get successful' - headers: { } - /threads-secure/states/{state}/: - get: - tags: - - StateThreadInfoAPI - description: 'Get threads for state' - responses: - '200': - description: 'Get successful' - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads-secure/states/{state}/metrics/: - get: - tags: - - StateThreadInfoAPI - description: 'Get metrics threads for state' - responses: - '200': - description: 'Get successful' - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads-secure/states/{state}/classes/: - get: - tags: - - StateThreadInfoAPI - description: 'Get threads class for state' - responses: - '200': - description: 'Get successful' - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] - /threads-secure/states/{state}/names/: - get: - tags: - - StateThreadInfoAPI - description: 'Get threads name for state' - responses: - '200': - description: 'Get successful' - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED] diff --git a/doc/WorkItemInfoAPI.yaml b/doc/WorkItemInfoAPI.yaml index d62ecd914d..423f710302 100644 --- a/doc/WorkItemInfoAPI.yaml +++ b/doc/WorkItemInfoAPI.yaml @@ -32,6 +32,86 @@ paths: '200': description: "Get successful" headers: { } + /work-items/priority/{priority}/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /work-items/priority/{priority}/metrics/: + get: + tags: + - WorkItemInfoAPI + description: 'Get metrics work items all filtering by priority' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /work-items/priority/{priority}/group-by/classes/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority and group by classes' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /work-items/priority/{priority}/group-by/classes/metrics/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority and group by classes then metrics' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] + /work-items/priority/{priority}/group-by/classes/count/: + get: + tags: + - WorkItemInfoAPI + description: 'Get work items all filtering by priority and group by classes then count' + responses: + '200': + description: "Get successful" + headers: { } + parameters: + - name: 'priority' + in: 'path' + required: true + schema: + type: 'string' + enum: [ HIGH, MEDIUM, LOW ] /work-items/group-by/classes/: get: tags: diff --git a/doc/WorkItemInfoSecureAPI.yaml b/doc/WorkItemInfoSecureAPI.yaml deleted file mode 100644 index d61842bb73..0000000000 --- a/doc/WorkItemInfoSecureAPI.yaml +++ /dev/null @@ -1,1274 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://localhost:8080/ScadaBR/api' - description: 'Scada development Server' - variables: {} -info: - version: 2.7.6.1 - title: Scada-LTS API via HTTPS - description: 'Scada-LTS OpenAPI Specification. Description contains only a SMS and Email new feature REST API' - termsOfService: '' - contact: - name: 'Kamil Jarmusik - SoftQ Developer' - email: 'kamil.jarmusik@softq.pl' - license: - name: 'GPL-2.0' -paths: - /work-items-secure/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed and not executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed and not executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed and not executed by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed and not executed by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed and not executed by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed and not executed by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed and not executed by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed and not executed by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items not executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items not executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items not executed by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items not executed by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items not executed by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items not executed by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items not executed by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/not-executed/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items not executed by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by class then statistic' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by priority then statistic' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items success' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items success' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items success by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items success by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items success by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items success by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items success by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/success/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items success by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/executed/longer/{executedMs}/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed longer than executedMs' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by class' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by class then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by priority' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/longer/{executedMs}/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed less than executedMs' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs by class' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs by class then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs by priority' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/less/{executedMs}/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed less than executedMs by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/executed/priority/{priority}/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed filtering by priority' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/executed/priority/{priority}/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed filtering by priority' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/executed/priority/{priority}/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed filtering by priority grouping by class' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/executed/priority/{priority}/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed filtering by priority grouping by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/executed/priority/{priority}/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed filtering by priority grouping by class then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/history/longer/{executedMs}/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs from History' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed longer than executedMs from History' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by class from History' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by class from History then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by class then count from History' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by priority from History' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by priority from History then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/longer/{executedMs}/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed longer than executedMs by priority then count from History' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'executedMs' - in: 'path' - required: true - schema: - type: 'integer' - /work-items-secure/history/priority/{priority}/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items all filtering by priority' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/history/priority/{priority}/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items all filtering by priority' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/history/priority/{priority}/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items all filtering by priority and group by classes' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/history/priority/{priority}/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items all filtering by priority and group by classes then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/history/priority/{priority}/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items all filtering by priority and group by classes then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'priority' - in: 'path' - required: true - schema: - type: 'string' - enum: [ HIGH, MEDIUM, LOW ] - /work-items-secure/history/process/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics process work items' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items and group by classes' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items and group by classes then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items and group by classes then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items and group by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items and group by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/process/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Get process work items and group by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items executed by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items fail executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items fail executed' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items fail executed by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items fail executed by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items fail executed by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items fail executed by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items fail executed by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/history/failed/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items fail executed by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/: - get: - tags: - - WorkItemInfoAPI - description: 'Get work items running' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Get metrics work items running' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/group-by/classes/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items running by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/group-by/classes/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items running by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/group-by/classes/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items running by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/group-by/priority/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items running by priority' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/group-by/priority/metrics/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items running by priority then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/running/group-by/priority/count/: - get: - tags: - - WorkItemInfoAPI - description: 'Grouping work items running by priority then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Get active scheduled work items' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/metrics/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Get metrics active scheduled work items' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/group-by/classes/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by class' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/group-by/classes/metrics/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/group-by/classes/count/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by class then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/group-by/states/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by states' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/group-by/states/metrics/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by states then metrics' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/group-by/states/count/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by states then count' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/states/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Get states scheduled work items' - responses: - '200': - description: "Get successful" - headers: { } - /work-items-secure/scheduled/states/{state}/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Get active scheduled work items' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - /work-items-secure/scheduled/states/{state}/metrics/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Get metrics active scheduled work items' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - /work-items-secure/scheduled/states/{state}/group-by/classes/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by class' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - /work-items-secure/scheduled/states/{state}/group-by/classes/metrics/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by class then metrics' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - /work-items-secure/scheduled/states/{state}/group-by/classes/count/: - get: - tags: - - ScheduledWorkItemInfoAPI - description: 'Grouping active scheduled work items by class then count' - responses: - '200': - description: "Get successful" - headers: { } - parameters: - - name: 'state' - in: 'path' - required: true - schema: - type: 'string' - enum: [ VIRGIN, CANCELLED, EXECUTED, SCHEDULED ] - diff --git a/scadalts-ui/src/locales/en.json b/scadalts-ui/src/locales/en.json index 59013031af..fb15c667e4 100644 --- a/scadalts-ui/src/locales/en.json +++ b/scadalts-ui/src/locales/en.json @@ -1055,5 +1055,9 @@ "userDetails.view.enableFullScreen": "Enable full screen mode", "userDetails.view.hideShortcutDisableFullScreen": "Hide shortcut to disable full screen", "systemsettings.event.pendingLimit": "Event Pending Limit", - "systemsettings.event.pendingCacheEnabled": "Enabled Event Pending Cache" + "systemsettings.event.pendingCacheEnabled": "Enabled Event Pending Cache", + "systemsettings.workitems.reporting.enabled": "Work items reporting enabled", + "systemsettings.workitems.reporting.itemspersecond.enabled": "Items per second reporting enabled", + "systemsettings.workitems.reporting.itemspersecond.limit": "Items per second reporting limit", + "systemsettings.threads.name.additional.length": "Thread name length" } diff --git a/scadalts-ui/src/views/System/SystemSettings/MiscSettingsComponent.vue b/scadalts-ui/src/views/System/SystemSettings/MiscSettingsComponent.vue index eb0cf2112f..816f087a31 100644 --- a/scadalts-ui/src/views/System/SystemSettings/MiscSettingsComponent.vue +++ b/scadalts-ui/src/views/System/SystemSettings/MiscSettingsComponent.vue @@ -61,6 +61,38 @@ dense > + + + + + + + + + + + + diff --git a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java index a7ca891303..5bf29b77c1 100644 --- a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java +++ b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java @@ -2,6 +2,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.scada_lts.mango.service.SystemSettingsService; import org.scada_lts.serorepl.utils.StringUtils; import org.scada_lts.utils.SystemSettingsUtils; import org.scada_lts.utils.ThreadUtils; @@ -11,13 +12,17 @@ import java.util.HashMap; import java.util.Map; +import static org.scada_lts.utils.TimeUtils.toMs; + public abstract class AbstractBeforeAfterWorkItem implements WorkItem, BeforeWork, AfterWork, AfterWork.WorkSuccessFail, AfterWork.WorkFinally { private static final Log LOG = LogFactory.getLog(AbstractBeforeAfterWorkItem.class); - private static final WorkItems ALL_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getWorkItemsLimit()); + private static final WorkItems ALL_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getWorkItemsLimit()); + private static final WorkItems HIGH_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryHighPriorityWorkItemsLimit()); + private static final WorkItems MEDIUM_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryMediumPriorityWorkItemsLimit()); + private static final WorkItems LOW_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryLowPriorityWorkItemsLimit()); private static final WorkItems HISTORY_EXECUTED_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryWorkItemsLimit()); - private static final WorkItems HISTORY_IDLE_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryWorkItemsLimit()); private static final WorkItems HISTORY_FAILED_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getFailedWorkItemsLimit()); private static final WorkItems HISTORY_PROCESS_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryProcessWorkItemsLimit()); private static final WorkItems HISTORY_HIGH_PRIORITY_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryHighPriorityWorkItemsLimit()); @@ -26,49 +31,56 @@ public abstract class AbstractBeforeAfterWorkItem implements WorkItem, BeforeWor private static final WorkItems HISTORY_EXECUTED_LONGER_WORK_ITEMS = new WorkItems(SystemSettingsUtils.getHistoryExecutedLongerWorkItemsLimit()); private static final WorkItems.RepeatAdd RUNNING_WORK_ITEMS = new WorkItems.RepeatAdd(SystemSettingsUtils.getRunningWorkItemsLimit(), SystemSettingsUtils.getRepeatRunningWorkItems()); private static final int EXECUTED_LONGER_WORK_ITEMS_THAN = SystemSettingsUtils.getHistoryExecutedLongerWorkItemsThan(); + private final SystemSettingsService systemSettingsService; + private final LocalDateTime createdDate = LocalDateTime.now(); private volatile boolean success = false; private volatile boolean workFailed = false; private volatile boolean running = false; private volatile String threadName = ""; private volatile String failedMessage = ""; - private final LocalDateTime createdDate = LocalDateTime.now(); private volatile LocalDateTime startedDate = null; private volatile LocalDateTime executedDate = null; - public static WorkItems.RepeatAdd getRunningWorkItems() { + static ReadWorkItems getRunningWorkItems() { return RUNNING_WORK_ITEMS; } - public static WorkItems getHistoryFailedWorkItems() { + static ReadWorkItems getHistoryFailedWorkItems() { return HISTORY_FAILED_WORK_ITEMS; } - public static WorkItems getHistoryExecutedLongerWorkItems() { + static ReadWorkItems getHistoryExecutedLongerWorkItems() { return HISTORY_EXECUTED_LONGER_WORK_ITEMS; } - public static WorkItems getHistoryProcessWorkItems() { + static ReadWorkItems getHistoryProcessWorkItems() { return HISTORY_PROCESS_WORK_ITEMS; } - public static WorkItems getHistoryHighPriorityWorkItems() { + static ReadWorkItems getHistoryHighPriorityWorkItems() { return HISTORY_HIGH_PRIORITY_WORK_ITEMS; } - public static WorkItems getHistoryMediumPriorityWorkItems() { + static ReadWorkItems getHistoryMediumPriorityWorkItems() { return HISTORY_MEDIUM_PRIORITY_WORK_ITEMS; } - public static WorkItems getHistoryLowPriorityWorkItems() { + static ReadWorkItems getHistoryLowPriorityWorkItems() { return HISTORY_LOW_PRIORITY_WORK_ITEMS; } - public static WorkItems getAllWorkItems() { - return ALL_WORK_ITEMS; - } - public static WorkItems getHistoryExecutedWorkItems() { + static ReadWorkItems getHistoryExecutedWorkItems() { return HISTORY_EXECUTED_WORK_ITEMS; } - - public static WorkItems getHistoryIdleWorkItems() { - return HISTORY_IDLE_WORK_ITEMS; + static ReadWorkItems getAllPriorityWorkItems() { + return ALL_PRIORITY_WORK_ITEMS; + } + static ReadWorkItems getLowWorkItems() { + return LOW_PRIORITY_WORK_ITEMS; + } + static ReadWorkItems getMediumWorkItems() { + return MEDIUM_PRIORITY_WORK_ITEMS; + } + static ReadWorkItems getHighWorkItems() { + return HIGH_PRIORITY_WORK_ITEMS; } - private static void addWorkItemAfterExecuted(WorkItem workItem, boolean failed, long executedMs) { - if(!SystemSettingsUtils.isWorkItemsReportingEnabled()) + private static void addWorkItemAfterExecuted(WorkItem workItem, boolean failed, long executedMs, + SystemSettingsService systemSettingsService) { + if(!isEnabled(systemSettingsService)) return; if(failed) HISTORY_FAILED_WORK_ITEMS.add(workItem); @@ -87,14 +99,11 @@ private static void addWorkItemAfterExecuted(WorkItem workItem, boolean failed, case LOW: HISTORY_LOW_PRIORITY_WORK_ITEMS.add(workItem); break; - case IDLE: - HISTORY_IDLE_WORK_ITEMS.add(workItem); - break; } HISTORY_EXECUTED_WORK_ITEMS.add(workItem); } - private static void addWorkItemIfNotRunning(WorkItem workItem, boolean running) { - if(!SystemSettingsUtils.isWorkItemsReportingEnabled()) + private static void addWorkItemIfNotRunning(WorkItem workItem, boolean running, SystemSettingsService systemSettingsService) { + if(!isEnabled(systemSettingsService)) return; if(running) { LOG.warn("Work items is running! : " + workItem); @@ -104,8 +113,21 @@ private static void addWorkItemIfNotRunning(WorkItem workItem, boolean running) } protected AbstractBeforeAfterWorkItem() { - if(SystemSettingsUtils.isWorkItemsReportingEnabled()) - ALL_WORK_ITEMS.add(this); + this.systemSettingsService = new SystemSettingsService(); + if(isEnabled(systemSettingsService)) { + switch (WorkItemPriority.priorityOf(getPriority())) { + case HIGH: + HIGH_PRIORITY_WORK_ITEMS.add(this); + break; + case MEDIUM: + MEDIUM_PRIORITY_WORK_ITEMS.add(this); + break; + case LOW: + LOW_PRIORITY_WORK_ITEMS.add(this); + break; + } + ALL_PRIORITY_WORK_ITEMS.add(this); + } } @Override @@ -117,7 +139,7 @@ public final void execute() { if(!StringUtils.isEmpty(suffix)) Thread.currentThread().setName(this.threadName + suffix); this.startedDate = LocalDateTime.now(); - addWorkItemIfNotRunning(this, runningNow); + addWorkItemIfNotRunning(this, runningNow, systemSettingsService); this.workFailed = false; this.executedDate = null; this.success = false; @@ -187,7 +209,7 @@ public final void execute() { this.failedMessage = msg; this.success = !failed; this.executedDate = LocalDateTime.now(); - addWorkItemAfterExecuted(this, failed, getExecutedMs()); + addWorkItemAfterExecuted(this, failed, getExecutedMs(), systemSettingsService); if(!StringUtils.isEmpty(suffix)) Thread.currentThread().setName(this.threadName); this.running = false; @@ -247,15 +269,7 @@ public boolean isRunning() { @Override public long getTimeInitMs() { - LocalDateTime started = this.startedDate; - if(started == null) - return -1; - try { - return ChronoUnit.MILLIS.between(this.createdDate, started); - } catch (Exception ex) { - LOG.warn(ex.getMessage() + " - " + this, ex); - return -1; - } + return toMs(getTimeInitNanos()); } @Override @@ -273,16 +287,7 @@ public long getTimeInitNanos() { @Override public long getExecutedMs() { - LocalDateTime started = this.startedDate; - LocalDateTime executed = this.executedDate; - if(started == null || executed == null) - return -1; - try { - return ChronoUnit.MILLIS.between(started, executed); - } catch (Exception ex) { - LOG.warn(ex.getMessage() + " - " + this, ex); - return -1; - } + return toMs(getExecutedNanos()); } @Override @@ -325,7 +330,7 @@ public LocalDateTime getStartedDate() { } private String suffixThreadName() { - return ThreadUtils.reduceName(" - " + WorkItemPriority.priorityOf(getPriority()) + " - " + getDetails()); + return ThreadUtils.reduceName(" - " + WorkItemPriority.priorityOf(getPriority()) + " - " + getDetails(), systemSettingsService); } private static String exceptionsToString(Map exceptions) { @@ -335,4 +340,8 @@ private static String exceptionsToString(Map exceptions) { } return sb.toString(); } + + private static boolean isEnabled(SystemSettingsService systemSettingsService) { + return systemSettingsService.isWorkItemsReportingEnabled(); + } } diff --git a/src/com/serotonin/mango/rt/maint/work/IdleWorkItem.java b/src/com/serotonin/mango/rt/maint/work/IdleWorkItem.java deleted file mode 100644 index d5aa70e6b5..0000000000 --- a/src/com/serotonin/mango/rt/maint/work/IdleWorkItem.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.serotonin.mango.rt.maint.work; - -public class IdleWorkItem extends AbstractBeforeAfterWorkItem { - - public IdleWorkItem() {} - - @Override - public void work() {} - - @Override - public int getPriority() { - return WorkItemPriority.IDLE.getPriority(); - } - - @Override - public String getDetails() { - return "idle"; - } -} diff --git a/src/com/serotonin/mango/rt/maint/work/ReadWorkItems.java b/src/com/serotonin/mango/rt/maint/work/ReadWorkItems.java new file mode 100644 index 0000000000..ad3ad1855f --- /dev/null +++ b/src/com/serotonin/mango/rt/maint/work/ReadWorkItems.java @@ -0,0 +1,11 @@ +package com.serotonin.mango.rt.maint.work; + + +import org.scada_lts.quartz.ReadItemsPerSecond; + +import java.util.List; + +public interface ReadWorkItems { + List get(); + ReadItemsPerSecond getItemsPerSecond(); +} \ No newline at end of file diff --git a/src/com/serotonin/mango/rt/maint/work/WorkItemExecute.java b/src/com/serotonin/mango/rt/maint/work/WorkItemExecute.java index 42d1931d70..d55c12297e 100644 --- a/src/com/serotonin/mango/rt/maint/work/WorkItemExecute.java +++ b/src/com/serotonin/mango/rt/maint/work/WorkItemExecute.java @@ -1,6 +1,5 @@ package com.serotonin.mango.rt.maint.work; -import org.scada_lts.quartz.ItemsPerSecond; import java.util.Objects; @@ -10,16 +9,13 @@ public class WorkItemExecute implements Comparable { private final long serial; private final WorkItem workItem; private final WorkItemPriority priority; - private final ItemsPerSecond itemsPerSecond; - public WorkItemExecute(WorkItem workItem, long serial, ItemsPerSecond itemsPerSecond) { - this.itemsPerSecond = itemsPerSecond; + public WorkItemExecute(WorkItem workItem, long serial) { this.className = workItem.getClass().getName(); this.workItem = workItem; this.serial = serial; this.priority = WorkItemPriority.priorityOf(workItem.getPriority()); } - public WorkItem getWorkItem() { return workItem; } @@ -36,22 +32,6 @@ public WorkItemPriority getPriority() { return priority; } - public long getItemsPerSecond() { - return itemsPerSecond.itemsPerSecond(); - } - - public long getItemsPerSecondOneMinute() { - return itemsPerSecond.itemsPerSecondFromOneMinute(); - } - - public long getItemsPerSecondFiveMinutes() { - return itemsPerSecond.itemsPerSecondFromFiveMinutes(); - } - - public long getItemsPerSecondFifteenMinutes() { - return itemsPerSecond.itemsPerSecondFromFifteenMinutes(); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/com/serotonin/mango/rt/maint/work/WorkItemPriority.java b/src/com/serotonin/mango/rt/maint/work/WorkItemPriority.java index 0aa75685b5..9649ea93cc 100644 --- a/src/com/serotonin/mango/rt/maint/work/WorkItemPriority.java +++ b/src/com/serotonin/mango/rt/maint/work/WorkItemPriority.java @@ -6,8 +6,7 @@ public enum WorkItemPriority { HIGH(WorkItem.PRIORITY_HIGH), MEDIUM(WorkItem.PRIORITY_MEDIUM), - LOW(WorkItem.PRIORITY_LOW), - IDLE(4); + LOW(WorkItem.PRIORITY_LOW); private final int priority; diff --git a/src/com/serotonin/mango/rt/maint/work/WorkItems.java b/src/com/serotonin/mango/rt/maint/work/WorkItems.java index 5585a8059a..052ccf7f83 100644 --- a/src/com/serotonin/mango/rt/maint/work/WorkItems.java +++ b/src/com/serotonin/mango/rt/maint/work/WorkItems.java @@ -1,7 +1,9 @@ package com.serotonin.mango.rt.maint.work; import org.scada_lts.quartz.ItemsPerSecond; +import org.scada_lts.quartz.ReadItemsPerSecond; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -11,7 +13,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -public class WorkItems { +class WorkItems implements ReadWorkItems { private final Map items; private final int limit; @@ -41,7 +43,7 @@ public void add(WorkItem item) { return; } itemsPerSecond.increment(); - WorkItemExecute execute = new WorkItemExecute(item, serial.incrementAndGet(), itemsPerSecond); + WorkItemExecute execute = new WorkItemExecute(item, serial.incrementAndGet()); int index = counter.incrementAndGet(); if (index >= limit) { counter.set(0); @@ -51,10 +53,14 @@ public void add(WorkItem item) { } } + @Override public List get() { - return items.values().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + return new ArrayList<>(items.values()); + } + + @Override + public ReadItemsPerSecond getItemsPerSecond() { + return itemsPerSecond; } @Override @@ -64,7 +70,7 @@ public String toString() { '}'; } - public static class RepeatAdd { + static class RepeatAdd implements ReadWorkItems { private final Map items; private final int limit; @@ -94,7 +100,7 @@ private void add(WorkItem item, Predicate repeatAddIf, int safe, long return; } itemsPerSecond.increment(); - WorkItemExecute execute = new WorkItemExecute(item, identifier == -1 ? serial.incrementAndGet() : identifier, itemsPerSecond); + WorkItemExecute execute = new WorkItemExecute(item, identifier == -1 ? serial.incrementAndGet() : identifier); int index = counter.incrementAndGet(); if(index >= limit) { counter.set(0); @@ -115,10 +121,14 @@ private void repeatAddIf(WorkItemExecute execute, Predicate repeatAddI } } + @Override public List get() { - return items.values().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + return new ArrayList<>(items.values()); + } + + @Override + public ReadItemsPerSecond getItemsPerSecond() { + return itemsPerSecond; } @Override diff --git a/src/com/serotonin/mango/rt/maint/work/WorkItemsUtils.java b/src/com/serotonin/mango/rt/maint/work/WorkItemsUtils.java index 7f7c349bac..4b78a78d6b 100644 --- a/src/com/serotonin/mango/rt/maint/work/WorkItemsUtils.java +++ b/src/com/serotonin/mango/rt/maint/work/WorkItemsUtils.java @@ -1,123 +1,55 @@ package com.serotonin.mango.rt.maint.work; -import com.serotonin.mango.Common; - -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - public final class WorkItemsUtils { private WorkItemsUtils() {} - public static List getCurrentAll() { - idle(); - return AbstractBeforeAfterWorkItem.getAllWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); - } - - public static List getCurrentNotExecuted() { - return getCurrentAll().stream() - .filter(a -> !a.getWorkItem().isExecuted()) - .collect(Collectors.toList()); - } - - public static List getCurrentExecuted() { - return getCurrentAll().stream() - .filter(a -> a.getWorkItem().isExecuted()) - .collect(Collectors.toList()); - } - - public static List getCurrentExecutedByPriority(WorkItemPriority priority) { - return getCurrentExecuted().stream() - .filter(a -> a.getPriority() == priority) - .collect(Collectors.toList()); - } - - public static List getCurrentExecutedLessThan(int executedMs) { - return getCurrentExecuted().stream() - .filter(a -> a.getWorkItem().getExecutedMs() < executedMs) - .collect(Collectors.toList()); + public static ReadWorkItems getCurrentAll() { + return AbstractBeforeAfterWorkItem.getAllPriorityWorkItems(); } - public static List getCurrentExecutedSuccess() { - return getCurrentExecuted().stream() - .filter(a -> a.getWorkItem().isSuccess()) - .collect(Collectors.toList()); + public static ReadWorkItems getCurrentHighPriority() { + return AbstractBeforeAfterWorkItem.getHighWorkItems(); } - public static List getCurrentExecutedLongerThan(int executedMs) { - return getCurrentExecuted().stream() - .filter(a -> a.getWorkItem().getExecutedMs() > executedMs) - .collect(Collectors.toList()); + public static ReadWorkItems getCurrentMediumPriority() { + return AbstractBeforeAfterWorkItem.getMediumWorkItems(); } - public static List getHistoryExecuted() { - idle(); - return AbstractBeforeAfterWorkItem.getHistoryExecutedWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getCurrentLowPriority() { + return AbstractBeforeAfterWorkItem.getLowWorkItems(); } - public static List getHistoryIdle() {; - return AbstractBeforeAfterWorkItem.getHistoryIdleWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryExecuted() { + return AbstractBeforeAfterWorkItem.getHistoryExecutedWorkItems(); } - - public static List getRunning() { - idle(); - return AbstractBeforeAfterWorkItem.getRunningWorkItems().get().stream() - .filter(a -> a.getWorkItem().isRunning()) - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getRunning() { + return AbstractBeforeAfterWorkItem.getRunningWorkItems(); } - public static List getHistoryFailed() { - return AbstractBeforeAfterWorkItem.getHistoryFailedWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryFailed() { + return AbstractBeforeAfterWorkItem.getHistoryFailedWorkItems(); } - public static List getHistoryExecutedLonger() { - return AbstractBeforeAfterWorkItem.getHistoryExecutedLongerWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryExecutedLonger() { + return AbstractBeforeAfterWorkItem.getHistoryExecutedLongerWorkItems(); } - public static List getHistoryByExecutedLongerThan(int executedMs) { - return getHistoryExecutedLonger().stream() - .filter(a -> a.getWorkItem().isExecuted() && a.getWorkItem().getExecutedMs() > executedMs) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryProcess() { + return AbstractBeforeAfterWorkItem.getHistoryProcessWorkItems(); } - public static List getHistoryProcess() { - return AbstractBeforeAfterWorkItem.getHistoryProcessWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryHighPriority() { + return AbstractBeforeAfterWorkItem.getHistoryHighPriorityWorkItems(); } - public static List getHistoryHighPriority() { - return AbstractBeforeAfterWorkItem.getHistoryHighPriorityWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryMediumPriority() { + return AbstractBeforeAfterWorkItem.getHistoryMediumPriorityWorkItems(); } - public static List getHistoryMediumPriority() { - return AbstractBeforeAfterWorkItem.getHistoryMediumPriorityWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); + public static ReadWorkItems getHistoryLowPriority() { + return AbstractBeforeAfterWorkItem.getHistoryLowPriorityWorkItems(); } - public static List getHistoryLowPriority() { - return AbstractBeforeAfterWorkItem.getHistoryLowPriorityWorkItems().get().stream() - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); - } - - public static void idle() { - Common.ctx.getBackgroundProcessing().addWorkItem(new IdleWorkItem()); - } } \ No newline at end of file diff --git a/src/com/serotonin/mango/web/dwr/SystemSettingsDwr.java b/src/com/serotonin/mango/web/dwr/SystemSettingsDwr.java index 1e1c4ede29..4802843ad4 100644 --- a/src/com/serotonin/mango/web/dwr/SystemSettingsDwr.java +++ b/src/com/serotonin/mango/web/dwr/SystemSettingsDwr.java @@ -164,6 +164,14 @@ public Map getSettings() { systemSettingsService.getMiscSettings().getEventPendingLimit()); settings.put(SystemSettingsDAO.EVENT_PENDING_CACHE_ENABLED, systemSettingsService.getMiscSettings().isEventPendingCacheEnabled()); + settings.put(SystemSettingsDAO.WORK_ITEMS_REPORTING_ENABLED, + systemSettingsService.getMiscSettings().isWorkItemsReportingEnabled()); + settings.put(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, + systemSettingsService.getMiscSettings().isWorkItemsReportingItemsPerSecondEnabled()); + settings.put(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, + systemSettingsService.getMiscSettings().getWorkItemsReportingItemsPerSecondLimit()); + settings.put(SystemSettingsDAO.THREADS_NAME_ADDITIONAL_LENGTH, + systemSettingsService.getMiscSettings().getThreadsNameAdditionalLength()); return settings; } @@ -313,7 +321,9 @@ public DwrResponseI18n saveHttpSettings(boolean useProxy, String host, int port, public DwrResponseI18n saveMiscSettings(int uiPerformance, String dataPointRtValueSynchronized, boolean viewEnableFullScreen, boolean viewHideShortcutDisableFullScreen, - int eventPendingLimit, boolean eventPendingCacheEnabled) { + int eventPendingLimit, boolean eventPendingCacheEnabled, + boolean workItemsReportingEnabled, boolean workItemsReportingItemsPerSecondEnabled, + int workItemsReportingItemsPerSecondLimit, int threadsNameAdditionalLength) { Permissions.ensureAdmin(); SystemSettingsDAO systemSettingsDAO = new SystemSettingsDAO(); DwrResponseI18n response = new DwrResponseI18n(); @@ -334,6 +344,28 @@ public DwrResponseI18n saveMiscSettings(int uiPerformance, String dataPointRtVal systemSettingsDAO.setIntValue(SystemSettingsDAO.EVENT_PENDING_LIMIT, eventPendingLimit); } systemSettingsDAO.setBooleanValue(SystemSettingsDAO.EVENT_PENDING_CACHE_ENABLED, eventPendingCacheEnabled); + if(eventPendingLimit < 0) { + response.addContextualMessage(SystemSettingsDAO.THREADS_NAME_ADDITIONAL_LENGTH, "validate.invalidValue"); + } else { + systemSettingsDAO.setIntValue(SystemSettingsDAO.THREADS_NAME_ADDITIONAL_LENGTH, threadsNameAdditionalLength); + } + systemSettingsDAO.setBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ENABLED, workItemsReportingEnabled); + if(workItemsReportingEnabled) { + systemSettingsDAO.setBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, workItemsReportingItemsPerSecondEnabled); + if(workItemsReportingItemsPerSecondEnabled) { + if (workItemsReportingItemsPerSecondLimit < 0) { + response.addContextualMessage(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, "validate.invalidValue"); + } else { + systemSettingsDAO.setIntValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, workItemsReportingItemsPerSecondLimit); + } + } else { + systemSettingsDAO.setIntValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, 0); + } + } else { + systemSettingsDAO.setBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, false); + systemSettingsDAO.setIntValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, 0); + } + return response; } diff --git a/src/com/serotonin/timer/AbstractTimer.java b/src/com/serotonin/timer/AbstractTimer.java index 03ce21fa2c..f68f6a5a37 100644 --- a/src/com/serotonin/timer/AbstractTimer.java +++ b/src/com/serotonin/timer/AbstractTimer.java @@ -1,6 +1,7 @@ package com.serotonin.timer; import org.apache.commons.lang3.StringUtils; +import org.scada_lts.mango.service.SystemSettingsService; import org.scada_lts.utils.ThreadUtils; import java.util.List; @@ -16,7 +17,7 @@ public void execute(Runnable command, String name) { if (StringUtils.isBlank(name)) execute(command); else - execute(new NamedRunnable(command, ThreadUtils.reduceName(name))); + execute(new NamedRunnable(command, ThreadUtils.reduceName(name, new SystemSettingsService()))); } abstract public void execute(ScheduledRunnable command, long fireTime); @@ -25,7 +26,7 @@ public void execute(ScheduledRunnable command, long fireTime, String name) { if (StringUtils.isBlank(name)) execute(command, fireTime); else - execute(new ScheduledNamedRunnable(command, ThreadUtils.reduceName(name)), fireTime); + execute(new ScheduledNamedRunnable(command, ThreadUtils.reduceName(name, new SystemSettingsService())), fireTime); } final public TimerTask schedule(TimerTask task) { diff --git a/src/org/scada_lts/dao/SystemSettingsDAO.java b/src/org/scada_lts/dao/SystemSettingsDAO.java index a39f7e6046..679e6fb16f 100644 --- a/src/org/scada_lts/dao/SystemSettingsDAO.java +++ b/src/org/scada_lts/dao/SystemSettingsDAO.java @@ -37,10 +37,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.*; import java.util.List; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; /** @@ -156,8 +155,11 @@ public class SystemSettingsDAO { public static final String VIEW_HIDE_SHORTCUT_DISABLE_FULL_SCREEN = "hideShortcutDisableFullScreen"; public static final String VIEW_FORCE_FULL_SCREEN_MODE = "viewForceFullScreenMode"; public static final String EVENT_PENDING_LIMIT = "eventPendingLimit"; - public static final String EVENT_PENDING_CACHE_ENABLED = "eventPendingCacheEnabled"; + public static final String WORK_ITEMS_REPORTING_ENABLED = "workItemsReportingEnabled"; + public static final String WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED = "workItemsReportingItemsPerSecondEnabled"; + public static final String WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT = "workItemsReportingItemsPerSecondLimit"; + public static final String THREADS_NAME_ADDITIONAL_LENGTH = "threadsNameAdditionalLength"; // @formatter:off private static final String SELECT_SETTING_VALUE_WHERE = "" + "select " @@ -188,13 +190,15 @@ public class SystemSettingsDAO { private static final Log LOG = LogFactory.getLog(SystemSettingsDAO.class); // Value cache - private static final Map cache = new HashMap(); + private static final Map cache = new ConcurrentHashMap<>(); public static String getValue(String key) { return getValue(key, (String) DEFAULT_VALUES.get(key)); } public static String getValue(String key, String defaultValue) { + if(key == null) + return null; String result = cache.get(key); if (result == null) { if (!cache.containsKey(key)) { @@ -203,10 +207,11 @@ public static String getValue(String key, String defaultValue) { } catch (EmptyResultDataAccessException e) { result = null; } - cache.put(key, result); if (result == null) { result = defaultValue; } + if(result != null) + cache.put(key, result); } else { result = defaultValue; } @@ -393,6 +398,10 @@ public String getDatabaseSchemaVersion(String key, String defaultValue) { DEFAULT_VALUES.put(VIEW_HIDE_SHORTCUT_DISABLE_FULL_SCREEN, SystemSettingsUtils.isHideShortcutDisableFullScreen()); DEFAULT_VALUES.put(EVENT_PENDING_LIMIT, SystemSettingsUtils.getEventPendingLimit()); DEFAULT_VALUES.put(EVENT_PENDING_CACHE_ENABLED, SystemSettingsUtils.isEventPendingCacheEnabled()); + DEFAULT_VALUES.put(WORK_ITEMS_REPORTING_ENABLED, SystemSettingsUtils.isWorkItemsReportingEnabled()); + DEFAULT_VALUES.put(WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, SystemSettingsUtils.isWorkItemsReportingItemsPerSecondEnabled()); + DEFAULT_VALUES.put(WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, SystemSettingsUtils.getWorkItemsReportingItemsPerSecondLimit()); + DEFAULT_VALUES.put(THREADS_NAME_ADDITIONAL_LENGTH, SystemSettingsUtils.getThreadsNameAdditionalLength()); } @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, rollbackFor = SQLException.class) diff --git a/src/org/scada_lts/dao/migration/mysql/V2_7_6_1__ChangeLengthLimitForSettingName.java b/src/org/scada_lts/dao/migration/mysql/V2_7_6_1__ChangeLengthLimitForSettingName.java new file mode 100644 index 0000000000..01a7ce108c --- /dev/null +++ b/src/org/scada_lts/dao/migration/mysql/V2_7_6_1__ChangeLengthLimitForSettingName.java @@ -0,0 +1,30 @@ +package org.scada_lts.dao.migration.mysql; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import org.scada_lts.dao.DAO; +import org.springframework.jdbc.core.JdbcTemplate; + +public class V2_7_6_1__ChangeLengthLimitForSettingName extends BaseJavaMigration { + + private static final Log LOG = LogFactory.getLog(V2_7_6_1__ChangeLengthLimitForSettingName.class); + + @Override + public void migrate(Context context) throws Exception { + + final JdbcTemplate jdbcTmp = DAO.getInstance().getJdbcTemp(); + + try { + migrate(jdbcTmp); + } catch (Exception ex) { + LOG.error(ex.getMessage(), ex); + throw ex; + } + } + + private void migrate(JdbcTemplate jdbcTmp) { + jdbcTmp.update("ALTER TABLE systemSettings MODIFY COLUMN settingName VARCHAR(255);"); + } +} diff --git a/src/org/scada_lts/mango/service/PointValueService.java b/src/org/scada_lts/mango/service/PointValueService.java index d3c2337df8..c437228715 100644 --- a/src/org/scada_lts/mango/service/PointValueService.java +++ b/src/org/scada_lts/mango/service/PointValueService.java @@ -625,7 +625,7 @@ public int getPriority() { @Override public String toString() { - return "BatchWriteBehind{" + entriesToString(ENTRIES, 100) + '}'; + return "BatchWriteBehind{entries size: " + ENTRIES.size() + ", instances size: " + instances.size() + '}'; } @Override @@ -753,18 +753,5 @@ public void updateAllMetaDataPointsByScript(User user) { .forEach(dp -> updateMetaDataPointByScript(user, dp.getXid())); } - - private static String entriesToString(ObjectQueue entries, int limit) { - StringBuilder entriesString = new StringBuilder(); - limit = Math.min(entries.size(), limit); - for(int i=0; i < limit; i++) { - try { - entriesString.append(entryInfo(entries.peek(i))).append(", "); - } catch (Exception ex) { - - } - } - return entriesString.toString(); - } } diff --git a/src/org/scada_lts/mango/service/SystemSettingsService.java b/src/org/scada_lts/mango/service/SystemSettingsService.java index 8bbfa106f0..fe9d7f370f 100644 --- a/src/org/scada_lts/mango/service/SystemSettingsService.java +++ b/src/org/scada_lts/mango/service/SystemSettingsService.java @@ -135,6 +135,10 @@ public JsonSettingsMisc getMiscSettings() { json.setEnableFullScreen(SystemSettingsDAO.getBooleanValue(SystemSettingsDAO.VIEW_FORCE_FULL_SCREEN_MODE, false)); json.setEventPendingLimit(SystemSettingsDAO.getIntValue(SystemSettingsDAO.EVENT_PENDING_LIMIT, 100)); json.setEventPendingCacheEnabled(SystemSettingsDAO.getBooleanValue(SystemSettingsDAO.EVENT_PENDING_CACHE_ENABLED, false)); + json.setThreadsNameAdditionalLength(SystemSettingsDAO.getIntValue(SystemSettingsDAO.THREADS_NAME_ADDITIONAL_LENGTH, 255)); + json.setWorkItemsReportingEnabled(SystemSettingsDAO.getBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ENABLED, true)); + json.setWorkItemsReportingItemsPerSecondEnabled(SystemSettingsDAO.getBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, true)); + json.setWorkItemsReportingItemsPerSecondLimit(SystemSettingsDAO.getIntValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, 20000)); return json; } @@ -145,6 +149,10 @@ public void saveMiscSettings(JsonSettingsMisc json) { systemSettingsDAO.setBooleanValue(SystemSettingsDAO.VIEW_FORCE_FULL_SCREEN_MODE, json.isEnableFullScreen()); systemSettingsDAO.setIntValue(SystemSettingsDAO.EVENT_PENDING_LIMIT, json.getEventPendingLimit()); systemSettingsDAO.setBooleanValue(SystemSettingsDAO.EVENT_PENDING_CACHE_ENABLED, json.isEventPendingCacheEnabled()); + systemSettingsDAO.setIntValue(SystemSettingsDAO.THREADS_NAME_ADDITIONAL_LENGTH, json.getThreadsNameAdditionalLength()); + systemSettingsDAO.setBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ENABLED, json.isWorkItemsReportingEnabled()); + systemSettingsDAO.setBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, json.isWorkItemsReportingItemsPerSecondEnabled()); + systemSettingsDAO.setIntValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, json.getWorkItemsReportingItemsPerSecondLimit()); } public SettingsDataRetention getDataRetentionSettings() { @@ -382,6 +390,46 @@ public Map getHttpResponseHeaders() { } } + public boolean isWorkItemsReportingEnabled() { + boolean defaultValue = SystemSettingsUtils.isWorkItemsReportingEnabled(); + try { + return SystemSettingsDAO.getBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ENABLED, defaultValue); + } catch (Exception e) { + LOG.error(e.getMessage()); + return defaultValue; + } + } + + public boolean isWorkItemsReportingItemsPerSecondEnabled() { + boolean defaultValue = SystemSettingsUtils.isWorkItemsReportingItemsPerSecondEnabled(); + try { + return SystemSettingsDAO.getBooleanValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED, defaultValue); + } catch (Exception e) { + LOG.error(e.getMessage()); + return defaultValue; + } + } + + public int getWorkItemsReportingItemsPerSecondLimit() { + int defaultValue = SystemSettingsUtils.getWorkItemsReportingItemsPerSecondLimit(); + try { + return SystemSettingsDAO.getIntValue(SystemSettingsDAO.WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT, defaultValue); + } catch (Exception e) { + LOG.error(e.getMessage()); + return defaultValue; + } + } + + public int getThreadsNameAdditionalLength() { + int defaultValue = SystemSettingsUtils.getThreadsNameAdditionalLength(); + try { + return SystemSettingsDAO.getIntValue(SystemSettingsDAO.THREADS_NAME_ADDITIONAL_LENGTH, defaultValue); + } catch (Exception e) { + LOG.error(e.getMessage()); + return defaultValue; + } + } + private static String getHttpResponseHeaders(JsonSettingsHttp json) { try { ObjectMapper objectMapper = new ObjectMapper(); diff --git a/src/org/scada_lts/quartz/ItemsPerSecond.java b/src/org/scada_lts/quartz/ItemsPerSecond.java index 9bbf8f31d2..da6aefa838 100644 --- a/src/org/scada_lts/quartz/ItemsPerSecond.java +++ b/src/org/scada_lts/quartz/ItemsPerSecond.java @@ -5,7 +5,7 @@ import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.StatefulJob; -import org.scada_lts.utils.SystemSettingsUtils; +import org.scada_lts.mango.service.SystemSettingsService; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -13,17 +13,18 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.IntPredicate; -public class ItemsPerSecond implements StatefulJob { +public class ItemsPerSecond implements StatefulJob, ReadItemsPerSecond { - private final Log LOG = LogFactory.getLog(ItemsPerSecond.class); + private static final Log LOG = LogFactory.getLog(ItemsPerSecond.class); private final Map itemsPerSecondMap; private final Map snapshots; private final AtomicInteger snapshotCounter; private final AtomicInteger itemsPerSecondCounter; + private final SystemSettingsService systemSettingsService; + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final int fromLastSeconds; private volatile int snapshotLastIndex; - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private ItemsPerSecond(int fromLastSeconds) { if (fromLastSeconds < 1) { @@ -34,34 +35,40 @@ private ItemsPerSecond(int fromLastSeconds) { this.snapshotCounter = new AtomicInteger(); this.itemsPerSecondCounter = new AtomicInteger(); this.fromLastSeconds = fromLastSeconds; + this.systemSettingsService = new SystemSettingsService(); } public static ItemsPerSecond fromFifteenMinutes() { ItemsPerSecond itemsPerSecond = new ItemsPerSecond(60*15); - if(isEnabled()) - EverySecond.schedule(itemsPerSecond); + EverySecond.schedule(itemsPerSecond); return itemsPerSecond; } + @Override public int itemsPerSecond() { return itemsPerSecond(1); } + @Override public int itemsPerSecondFromOneMinute() { return itemsPerSecond(60); } + @Override public int itemsPerSecondFromFiveMinutes() { return itemsPerSecond(60*5); } + @Override public int itemsPerSecondFromFifteenMinutes() { return itemsPerSecond(60*15); } + @Override public int itemsPerSecond(int fromLastSeconds) { - if(!isEnabled()) + if(isDisabled(systemSettingsService, true)) { return -1; + } if (fromLastSeconds > this.fromLastSeconds) { throw new IllegalArgumentException("The value fromLastSeconds cannot be greater than: " + this.fromLastSeconds + ", but was: " + fromLastSeconds); } @@ -81,15 +88,17 @@ public int itemsPerSecond(int fromLastSeconds) { } public void increment() { - if(!isEnabled()) + if(isDisabled(systemSettingsService, false)) { return; - update(System.currentTimeMillis(), itemsPerSecondCounter, index -> index >= SystemSettingsUtils.getWorkItemsReportingItemsPerSecondLimit(), itemsPerSecondMap); + } + update(System.currentTimeMillis(), itemsPerSecondCounter, index -> index >= systemSettingsService.getWorkItemsReportingItemsPerSecondLimit(), itemsPerSecondMap); } @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { - if(!isEnabled()) + if(isDisabled(systemSettingsService, false)) { return; + } long currentTime = System.currentTimeMillis(); itemsPerSecondMap.entrySet().removeIf(time -> currentTime - time.getValue() > 1000L); int result = itemsPerSecondMap.size(); @@ -117,22 +126,27 @@ private static int update(T result, AtomicInteger counter, IntPredicate rese } } - private static int itemsPerSecond(int fromLastSeconds, Map states, int snapshotLastIndex) { + private static int itemsPerSecond(int fromLastSeconds, Map snapshots, int snapshotLastIndex) { List toCalc = new ArrayList<>(); int currentIndex = snapshotLastIndex; for(int i = 0; i < fromLastSeconds; i++) { if(currentIndex < 0) { - currentIndex = states.size() + currentIndex; + currentIndex = snapshots.size() + currentIndex; } - Integer stat = states.get(currentIndex); + Integer stat = snapshots.get(currentIndex); toCalc.add(Objects.requireNonNullElse(stat, 0)); --currentIndex; } return toCalc.stream().mapToInt(a -> a).sum() / fromLastSeconds; } - private static boolean isEnabled() { - return SystemSettingsUtils.isWorkItemsReportingItemsPerSecondEnabled() && SystemSettingsUtils.getWorkItemsReportingItemsPerSecondLimit() > 0; + private static boolean isDisabled(SystemSettingsService systemSettingsService, boolean log) { + boolean disabled = !systemSettingsService.isWorkItemsReportingEnabled() + || !systemSettingsService.isWorkItemsReportingItemsPerSecondEnabled() + || systemSettingsService.getWorkItemsReportingItemsPerSecondLimit() <= 0; + if(log && disabled) { + LOG.warn("Reporting items per second is disabled"); + } + return disabled; } - } \ No newline at end of file diff --git a/src/org/scada_lts/quartz/ReadItemsPerSecond.java b/src/org/scada_lts/quartz/ReadItemsPerSecond.java new file mode 100644 index 0000000000..550a87df73 --- /dev/null +++ b/src/org/scada_lts/quartz/ReadItemsPerSecond.java @@ -0,0 +1,9 @@ +package org.scada_lts.quartz; + +public interface ReadItemsPerSecond { + int itemsPerSecond(); + int itemsPerSecondFromOneMinute(); + int itemsPerSecondFromFiveMinutes(); + int itemsPerSecondFromFifteenMinutes(); + int itemsPerSecond(int fromLastSeconds); +} diff --git a/src/org/scada_lts/utils/ApiUtils.java b/src/org/scada_lts/utils/ApiUtils.java index 033a825d61..e9492d8313 100644 --- a/src/org/scada_lts/utils/ApiUtils.java +++ b/src/org/scada_lts/utils/ApiUtils.java @@ -116,14 +116,33 @@ public static UserInfo toUserInfo(User user) { } public static Map convertMap(Map> map, Function, R> converter) { - Map result = new LinkedHashMap<>(); - for(Map.Entry> entry: map.entrySet()) { - result.put(entry.getKey(), converter.apply(entry.getValue())); - } - return result; + return map.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, a -> converter.apply(a.getValue()), + (v1, v2) -> { throw new IllegalStateException(); }, LinkedHashMap::new + )); + } + + public static Map convertMap(Map> map, Function, R> converter, Comparator>> comparator) { + return map.entrySet().stream() + .sorted(comparator.reversed()) + .collect(Collectors.toMap(Map.Entry::getKey, a -> converter.apply(a.getValue()), + (v1, v2) -> { throw new IllegalStateException(); }, LinkedHashMap::new + )); + } + + public static Map sortMap(Map map, Comparator> comparator) { + return map.entrySet().stream() + .sorted(comparator.reversed()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, + (v1, v2) -> { throw new IllegalStateException(); }, LinkedHashMap::new + )); } public static R convertList(List list, Function, R> converter) { return converter.apply(list); } + + public static R convertList(T list, Function converter) { + return converter.apply(list); + } } diff --git a/src/org/scada_lts/utils/SystemSettingsUtils.java b/src/org/scada_lts/utils/SystemSettingsUtils.java index 0dc1e1b562..8628ed3a80 100644 --- a/src/org/scada_lts/utils/SystemSettingsUtils.java +++ b/src/org/scada_lts/utils/SystemSettingsUtils.java @@ -43,6 +43,7 @@ private SystemSettingsUtils() {} public static final String WORK_ITEMS_REPORTING_ENABLED_KEY = "workitems.reporting.enabled"; public static final String WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_ENABLED_KEY = "workitems.reporting.itemspersecond.enabled"; public static final String WORK_ITEMS_REPORTING_ITEMS_PER_SECOND_LIMIT_KEY = "workitems.reporting.itemspersecond.limit"; + public static final String THREADS_NAME_ADDITIONAL_LENGTH_KEY = "threads.name.additional.length"; private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(SystemSettingsUtils.class); @@ -322,4 +323,14 @@ public static int getWorkItemsReportingItemsPerSecondLimit() { return 20000; } } + + public static int getThreadsNameAdditionalLength() { + try { + String config = ScadaConfig.getInstance().getConf().getProperty(THREADS_NAME_ADDITIONAL_LENGTH_KEY, "255"); + return Integer.parseInt(config); + } catch (Exception e) { + LOG.error(e.getMessage()); + return 255; + } + } } diff --git a/src/org/scada_lts/utils/ThreadUtils.java b/src/org/scada_lts/utils/ThreadUtils.java index 111991d9db..82ecbe8a32 100644 --- a/src/org/scada_lts/utils/ThreadUtils.java +++ b/src/org/scada_lts/utils/ThreadUtils.java @@ -1,20 +1,18 @@ package org.scada_lts.utils; -import org.scada_lts.config.ScadaConfig; - -import java.io.IOException; +import org.scada_lts.mango.service.SystemSettingsService; public final class ThreadUtils { private ThreadUtils() {} - public static String reduceName(String name) { + public static String reduceName(String name, SystemSettingsService systemSettingsService) { try { - int limit = ScadaConfig.getInstance().getInt("thread.name.additional.length", 0); - if(limit == 0) + int length = systemSettingsService.getThreadsNameAdditionalLength(); + if(length == 0) return ""; - return name.length() > limit ? name.substring(0, limit) + ".." : name; - } catch (IOException e) { + return name.length() > length ? name.substring(0, length) + ".." : name; + } catch (Exception e) { return ""; } } diff --git a/src/org/scada_lts/utils/TimeUtils.java b/src/org/scada_lts/utils/TimeUtils.java new file mode 100644 index 0000000000..808156a4fe --- /dev/null +++ b/src/org/scada_lts/utils/TimeUtils.java @@ -0,0 +1,13 @@ +package org.scada_lts.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class TimeUtils { + + public static long toMs(long nanos) { + if(nanos == -1) + return -1; + return BigDecimal.valueOf(nanos).divide(BigDecimal.valueOf(1000000L), RoundingMode.HALF_DOWN).longValue(); + } +} diff --git a/src/org/scada_lts/web/mvc/api/ThreadInfoSecureAPI.java b/src/org/scada_lts/web/mvc/api/ThreadInfoSecureAPI.java index 9fbc732e30..2796a5b9ac 100644 --- a/src/org/scada_lts/web/mvc/api/ThreadInfoSecureAPI.java +++ b/src/org/scada_lts/web/mvc/api/ThreadInfoSecureAPI.java @@ -16,7 +16,7 @@ @RestController -@RequestMapping(path = "/api/threads-secure") +@RequestMapping(path = "/api/secure/threads") public class ThreadInfoSecureAPI { private final ThreadInfoAPI threadInfoAPI; diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java index 8f71a9a378..128b17725e 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoAPI.java @@ -34,6 +34,41 @@ public ResponseEntity getCurrentWorkItemsMetrics(HttpServletRe return new ResponseEntity<>(response.onlyMetrics(), HttpStatus.OK); } + @GetMapping(value = "/priority/{priority}") + public ResponseEntity getCurrentWorkItemsByPriority(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + WorkItemInfoList response = workItemInfoApiService.getCurrentWorkItemsByPriority(request, priority); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/priority/{priority}/metrics") + public ResponseEntity getCurrentWorkItemsByPriorityMetrics(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + WorkItemInfoList response = workItemInfoApiService.getCurrentWorkItemsByPriority(request, priority); + return new ResponseEntity<>(response.onlyMetrics(), HttpStatus.OK); + } + + @GetMapping(value = "/priority/{priority}/group-by/classes") + public ResponseEntity> getCurrentWorkItemsByPriorityGroupByClassName(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + Map response = workItemInfoApiService.getCurrentWorkItemsByPriorityGroupByClassName(request, priority); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/priority/{priority}/group-by/classes/metrics") + public ResponseEntity> getCurrentWorkItemsByPriorityGroupByClassNameMetrics(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + Map response = workItemInfoApiService.getCurrentWorkItemsByPriorityGroupByClassNameMetrics(request, priority); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @GetMapping(value = "/priority/{priority}/group-by/classes/count") + public ResponseEntity> getCurrentWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + Mapresponse = workItemInfoApiService.getCurrentWorkItemsByPriorityGroupByClassNameCount(request, priority); + return new ResponseEntity<>(response, HttpStatus.OK); + } + @GetMapping(value = "/group-by/classes") public ResponseEntity> getCurrentWorkItemsGroupByClassName(HttpServletRequest request) { Map response = workItemInfoApiService.getCurrentWorkItemsGroupByClassName(request); diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java index d227d4a64d..223a6c3c21 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoApiService.java @@ -1,6 +1,7 @@ package org.scada_lts.web.mvc.api; import com.serotonin.mango.rt.maint.work.*; +import org.scada_lts.quartz.ReadItemsPerSecond; import org.scada_lts.web.mvc.api.exceptions.InternalServerErrorException; import org.scada_lts.web.mvc.api.json.WorkItemInfo; import org.scada_lts.web.mvc.api.json.WorkItemInfoList; @@ -10,293 +11,346 @@ import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; -import static org.scada_lts.utils.ApiUtils.convertList; import static org.scada_lts.utils.ApiUtils.convertMap; +import static org.scada_lts.utils.ApiUtils.sortMap; import static org.scada_lts.utils.ValidationUtils.checkIfNonAdminThenUnauthorized; +import static org.scada_lts.web.mvc.api.WorkItemInfoApiService.Comparators.byExecuteMsComparator; +import static org.scada_lts.web.mvc.api.WorkItemInfoApiService.Comparators.bySerialComparator; +import static org.scada_lts.web.mvc.api.WorkItemInfoApiService.Conditions.*; @Service public class WorkItemInfoApiService { public WorkItemInfoList getCurrentWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getCurrentAll), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, + WorkItemsUtils::getCurrentAll, WorkItemInfoList::new); } public Map getCurrentWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getCurrentAll); + return get(request, GroupBy::className, WorkItemsUtils::getCurrentAll); } public Map getCurrentWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getCurrentAll); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll); } public Map getCurrentWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getCurrentAll); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll); } public Map getCurrentWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getCurrentAll); + return get(request, GroupBy::priority, WorkItemsUtils::getCurrentAll); } public Map getCurrentWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getCurrentAll); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll); } public Map getCurrentWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getCurrentAll); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getCurrentAll); } public WorkItemInfoList getCurrentExecutedWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getCurrentExecuted), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentAll, + isExecuted(), WorkItemInfoList::new); } public Map getCurrentExecutedWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getCurrentExecuted); + return get(request, GroupBy::className, WorkItemsUtils::getCurrentAll, isExecuted()); } public Map getCurrentExecutedWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getCurrentExecuted); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll, isExecuted()); } public Map getCurrentExecutedWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getCurrentExecuted); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll, isExecuted()); } public Map getCurrentExecutedWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getCurrentExecuted); + return get(request, GroupBy::priority, WorkItemsUtils::getCurrentAll, isExecuted()); } public Map getCurrentExecutedWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getCurrentExecuted); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll, isExecuted()); } public Map getCurrentExecutedWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getCurrentExecuted); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getCurrentAll, isExecuted()); } public WorkItemInfoList getCurrentExecutedSuccessWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getCurrentExecutedSuccess), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentAll, + isExecuted().and(isSuccess()), WorkItemInfoList::new); } public Map getCurrentExecutedSuccessWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getCurrentExecutedSuccess); + return get(request, GroupBy::className, WorkItemsUtils::getCurrentAll, isExecuted().and(isSuccess())); } public Map getCurrentExecutedSuccessWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getCurrentExecutedSuccess); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isSuccess())); } public Map getCurrentExecutedSuccessWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getCurrentExecutedSuccess); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isSuccess())); } public Map getCurrentExecutedSuccessWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getCurrentExecutedSuccess); + return get(request, GroupBy::priority, WorkItemsUtils::getCurrentAll, isExecuted().and(isSuccess())); } public Map getCurrentExecutedSuccessWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getCurrentExecutedSuccess); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isSuccess())); } public Map getCurrentExecutedSuccessWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getCurrentExecutedSuccess); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isSuccess())); } public WorkItemInfoList getCurrentExecutedLessWorkItems(HttpServletRequest request, int executedMs) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentAll, + isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator(), WorkItemInfoList::new); } public Map getCurrentExecutedLessWorkItemsGroupByClassName(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassName, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs); + return get(request, GroupBy::className, WorkItemsUtils::getCurrentAll, isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLessWorkItemsGroupByClassNameMetrics(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator()); } - public Map getCurrentExecutedLessWorkItemsGroupByClassNameCount(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator()); + } public Map getCurrentExecutedLessWorkItemsGroupByPriority(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriority, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs); + return get(request, GroupBy::priority, WorkItemsUtils::getCurrentAll, isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLessWorkItemsGroupByPriorityMetrics(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLessWorkItemsGroupByPriorityCount(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLessThan, executedMs); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isLessThan(executedMs)), byExecuteMsComparator()); } public WorkItemInfoList getCurrentExecutedLongerWorkItems(HttpServletRequest request, int executedMs) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentAll, + isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator(), WorkItemInfoList::new); } public Map getCurrentExecutedLongerWorkItemsGroupByClassName(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassName, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLongerWorkItemsGroupByClassNameMetrics(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLongerWorkItemsGroupByClassNameCount(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLongerWorkItemsGroupByPriority(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriority, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs); + return get(request, GroupBy::priority, WorkItemsUtils::getCurrentAll, isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLongerWorkItemsGroupByPriorityMetrics(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator()); } public Map getCurrentExecutedLongerWorkItemsGroupByPriorityCount(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, byExecuteMsComparator(), - WorkItemsUtils::getCurrentExecutedLongerThan, executedMs); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isLongerThan(executedMs)), byExecuteMsComparator()); } public WorkItemInfoList getCurrentExecutedWorkItemsByPriority(HttpServletRequest request, WorkItemPriority priority) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, bySerialComparator(), - WorkItemsUtils::getCurrentExecutedByPriority, priority), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentAll, + isExecuted().and(isPriority(priority)), WorkItemInfoList::new); + } + + public WorkItemInfoList getCurrentWorkItemsByPriority(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentHighPriority, WorkItemInfoList::new); + case MEDIUM: + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentMediumPriority, WorkItemInfoList::new); + case LOW: + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentLowPriority, WorkItemInfoList::new); + default: + return new WorkItemInfoList(Collections.emptyList(), null); + } } public Map getCurrentExecutedWorkItemsByPriorityGroupByClassName(HttpServletRequest request, WorkItemPriority priority) { - return get(request, WorkItemInfoApiService::groupByClassName, bySerialComparator(), - WorkItemsUtils::getCurrentExecutedByPriority, priority); + return get(request, GroupBy::className, WorkItemsUtils::getCurrentAll, isExecuted().and(isPriority(priority))); + } public Map getCurrentExecutedWorkItemsByPriorityGroupByClassNameMetrics(HttpServletRequest request, WorkItemPriority priority) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, bySerialComparator(), - WorkItemsUtils::getCurrentExecutedByPriority, priority); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll, isExecuted().and(isPriority(priority))); } public Map getCurrentExecutedWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, WorkItemPriority priority) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, bySerialComparator(), - WorkItemsUtils::getCurrentExecutedByPriority, priority); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll, isExecuted().and(isPriority(priority))); } public WorkItemInfoList getCurrentNotExecutedWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getCurrentNotExecuted), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getCurrentAll, + isNotExecuted(), WorkItemInfoList::new); } public Map getCurrentNotExecutedWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getCurrentNotExecuted); + return get(request, GroupBy::className, WorkItemsUtils::getCurrentAll, isNotExecuted()); } public Map getCurrentNotExecutedWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getCurrentNotExecuted); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentAll, isNotExecuted()); } public Map getCurrentNotExecutedWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getCurrentNotExecuted); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentAll, isNotExecuted()); } public Map getCurrentNotExecutedWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getCurrentNotExecuted); + return get(request, GroupBy::priority, WorkItemsUtils::getCurrentAll, isNotExecuted()); } public Map getCurrentNotExecutedWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getCurrentNotExecuted); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getCurrentAll, isNotExecuted()); } - public Map getCurrentNotExecutedWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getCurrentNotExecuted); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getCurrentAll, isNotExecuted()); } public WorkItemInfoList getHistoryProcessWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryProcess), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, + WorkItemsUtils::getHistoryProcess, WorkItemInfoList::new); } public Map getHistoryProcessWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryProcess); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryProcess); } public Map getHistoryProcessWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryProcess); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryProcess); } public Map getHistoryProcessWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryProcess); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryProcess); } public Map getHistoryProcessWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getHistoryProcess); + return get(request, GroupBy::priority, WorkItemsUtils::getHistoryProcess); } public Map getHistoryProcessWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getHistoryProcess); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getHistoryProcess); } public Map getHistoryProcessWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getHistoryProcess); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getHistoryProcess); } public WorkItemInfoList getHistoryWorkItemsByPriority(HttpServletRequest request, WorkItemPriority priority) { - return convertList(getWorkItems(request, priority), WorkItemInfoList::new); + switch (priority) { + case HIGH: + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getHistoryHighPriority, WorkItemInfoList::new); + case MEDIUM: + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getHistoryMediumPriority, WorkItemInfoList::new); + case LOW: + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getHistoryLowPriority, WorkItemInfoList::new); + default: + return new WorkItemInfoList(new ArrayList<>(), null); + } } public WorkItemInfoList getHistoryExecutedWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryExecuted), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getHistoryExecuted, WorkItemInfoList::new); } public Map getHistoryExecutedWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryExecuted); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryExecuted); } public Map getHistoryExecutedWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryExecuted); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryExecuted); } public Map getHistoryExecutedWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryExecuted); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryExecuted); } public Map getHistoryExecutedWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getHistoryExecuted); + return get(request, GroupBy::priority, WorkItemsUtils::getHistoryExecuted); } public Map getHistoryExecutedWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getHistoryExecuted); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getHistoryExecuted); } public Map getHistoryExecutedWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getHistoryExecuted); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getHistoryExecuted); + } + + public Map getCurrentWorkItemsByPriorityGroupByClassName(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return get(request, GroupBy::className, WorkItemsUtils::getCurrentHighPriority); + case MEDIUM: + return get(request, GroupBy::className, WorkItemsUtils::getCurrentMediumPriority); + case LOW: + return get(request, GroupBy::className, WorkItemsUtils::getCurrentLowPriority); + default: + return Collections.emptyMap(); + } + } + + public Map getCurrentWorkItemsByPriorityGroupByClassNameMetrics(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentHighPriority); + case MEDIUM: + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentMediumPriority); + case LOW: + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getCurrentLowPriority); + default: + return Collections.emptyMap(); + } + } + + public Map getCurrentWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, WorkItemPriority priority) { + switch (priority) { + case HIGH: + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentHighPriority); + case MEDIUM: + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentMediumPriority); + case LOW: + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getCurrentLowPriority); + default: + return Collections.emptyMap(); + } } public Map getHistoryWorkItemsByPriorityGroupByClassName(HttpServletRequest request, WorkItemPriority priority) { switch (priority) { case HIGH: - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryHighPriority); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryHighPriority); case MEDIUM: - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryMediumPriority); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryMediumPriority); case LOW: - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryLowPriority); - case IDLE: - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryIdle); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryLowPriority); default: return Collections.emptyMap(); } @@ -305,13 +359,11 @@ public Map getHistoryWorkItemsByPriorityGroupByClassNa public Map getHistoryWorkItemsByPriorityGroupByClassNameMetrics(HttpServletRequest request, WorkItemPriority priority) { switch (priority) { case HIGH: - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryHighPriority); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryHighPriority); case MEDIUM: - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryMediumPriority); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryMediumPriority); case LOW: - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryLowPriority); - case IDLE: - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryIdle); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryLowPriority); default: return Collections.emptyMap(); } @@ -320,231 +372,284 @@ public Map getHistoryWorkItemsByPriorityGroupByClassNa public Map getHistoryWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, WorkItemPriority priority) { switch (priority) { case HIGH: - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryHighPriority); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryHighPriority); case MEDIUM: - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryMediumPriority); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryMediumPriority); case LOW: - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryLowPriority); - case IDLE: - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryIdle); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryLowPriority); default: return Collections.emptyMap(); } } public WorkItemInfoList getHistoryExecutedFailedWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryFailed), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getHistoryFailed, WorkItemInfoList::new); } public Map getHistoryExecutedFailedWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getHistoryFailed); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryFailed); } public Map getHistoryExecutedFailedWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getHistoryFailed); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryFailed); } public Map getHistoryExecutedFailedWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getHistoryFailed); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryFailed); } public Map getHistoryExecutedFailedWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getHistoryFailed); + return get(request, GroupBy::priority, WorkItemsUtils::getHistoryFailed); } public Map getHistoryExecutedFailedWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getHistoryFailed); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getHistoryFailed); } public Map getHistoryExecutedFailedWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getHistoryFailed); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getHistoryFailed); } public WorkItemInfoList getHistoryExecutedLongerWorkItems(HttpServletRequest request, int executedMs) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getHistoryExecutedLonger, + isLongerThan(executedMs), byExecuteMsComparator(), WorkItemInfoList::new); } public Map getHistoryExecutedLongerWorkItemsGroupByClassName(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassName, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs); + return get(request, GroupBy::className, WorkItemsUtils::getHistoryExecutedLonger, isLongerThan(executedMs), byExecuteMsComparator()); } public Map getHistoryExecutedLongerWorkItemsGroupByClassNameMetrics(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getHistoryExecutedLonger, isLongerThan(executedMs), byExecuteMsComparator()); } public Map getHistoryExecutedLongerWorkItemsGroupByClassNameCount(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getHistoryExecutedLonger, isLongerThan(executedMs), byExecuteMsComparator()); } public Map getHistoryExecutedLongerWorkItemsGroupByPriority(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriority, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs); + return get(request, GroupBy::priority, WorkItemsUtils::getHistoryExecutedLonger, isLongerThan(executedMs), byExecuteMsComparator()); } public Map getHistoryExecutedLongerWorkItemsGroupByPriorityMetrics(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getHistoryExecutedLonger, isLongerThan(executedMs), byExecuteMsComparator()); } public Map getHistoryExecutedLongerWorkItemsGroupByPriorityCount(HttpServletRequest request, int executedMs) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, byExecuteMsComparator(), - WorkItemsUtils::getHistoryByExecutedLongerThan, executedMs); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getHistoryExecutedLonger, isLongerThan(executedMs), byExecuteMsComparator()); } public WorkItemInfoList getRunningWorkItems(HttpServletRequest request) { - return convertList(get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getRunning), WorkItemInfoList::new); + return getTo(request, WorkItemInfoApiService::convertToWorkItemInfoList, WorkItemsUtils::getRunning, + isRunning(), WorkItemInfoList::new); } public Map getRunningWorkItemsGroupByClassName(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassName, WorkItemsUtils::getRunning); + return get(request, GroupBy::className, WorkItemsUtils::getRunning, isRunning()); } public Map getRunningWorkItemsGroupByClassNameMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameMetrics, WorkItemsUtils::getRunning); + return get(request, GroupBy::classNameMetrics, WorkItemsUtils::getRunning, isRunning()); } public Map getRunningWorkItemsGroupByClassNameCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByClassNameCounting, WorkItemsUtils::getRunning); + return get(request, GroupBy::classNameCounting, WorkItemsUtils::getRunning, isRunning()); } public Map getRunningWorkItemsGroupByPriority(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriority, WorkItemsUtils::getRunning); + return get(request, GroupBy::priority, WorkItemsUtils::getRunning, isRunning()); } public Map getRunningWorkItemsGroupByPriorityMetrics(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityMetrics, WorkItemsUtils::getRunning); + return get(request, GroupBy::priorityMetrics, WorkItemsUtils::getRunning, isRunning()); } public Map getRunningWorkItemsGroupByPriorityCount(HttpServletRequest request) { - return get(request, WorkItemInfoApiService::groupByPriorityCounting, WorkItemsUtils::getRunning); + return get(request, GroupBy::priorityCounting, WorkItemsUtils::getRunning, isRunning()); + } + + private static R getTo(HttpServletRequest request, + BiFunction, Comparator, T> preparing, + Supplier data, + BiFunction converter) { + return getTo(request, preparing, data, a -> true, bySerialComparator(), converter); + } + + private static R getTo(HttpServletRequest request, + BiFunction, Comparator, T> preparing, + Supplier data, + Predicate filtering, + BiFunction converter) { + return getTo(request, preparing, data, filtering, bySerialComparator(), converter); + } + + private static R getTo(HttpServletRequest request, + BiFunction, Comparator, T> preparing, + Supplier data, + Predicate filtering, + Comparator comparator, + BiFunction converter) { + checkIfNonAdminThenUnauthorized(request); + try { + ReadWorkItems workItems = data.get(); + return converter.apply(preparing.apply(filtering(workItems.get(), filtering), comparator.thenComparing(Comparator.reverseOrder())), workItems.getItemsPerSecond()); + } catch (Exception e) { + throw new InternalServerErrorException(e, request.getRequestURI()); + } + } + + private static T get(HttpServletRequest request, + BiFunction, ReadItemsPerSecond, T> preparing, + Supplier data) { + return get(request, preparing, data, a -> true, bySerialComparator()); + } + + private static T get(HttpServletRequest request, + BiFunction, ReadItemsPerSecond, T> preparing, + Supplier data, + Predicate filtering) { + return get(request, preparing, data, filtering, bySerialComparator()); + } + + private static T get(HttpServletRequest request, + Function, T> preparing, + Supplier data) { + return get(request, preparing, data, a -> true, bySerialComparator()); + } + + private static T get(HttpServletRequest request, + Function, T> preparing, + Supplier data, + Predicate filtering) { + return get(request, preparing, data, filtering, bySerialComparator()); } private static T get(HttpServletRequest request, - Function, T> preparing, - Supplier> data) { + Function, T> preparing, + Supplier data, + Predicate filtering, + Comparator comparator) { checkIfNonAdminThenUnauthorized(request); try { - List workItems = data.get(); - return preparing.apply(workItems); + ReadWorkItems workItems = data.get(); + return preparing.apply(convertToWorkItemInfoList(filtering(workItems.get(), filtering), comparator.thenComparing(Comparator.reverseOrder()))); } catch (Exception e) { throw new InternalServerErrorException(e, request.getRequestURI()); } } - private static T get(HttpServletRequest request, - BiFunction, R, T> preparing, R preparingArg, - Function> data, S dataArg) { + private static T get(HttpServletRequest request, + BiFunction, ReadItemsPerSecond, T> preparing, + Supplier data, + Predicate filtering, + Comparator comparator) { checkIfNonAdminThenUnauthorized(request); try { - List workItems = data.apply(dataArg); - return preparing.apply(workItems, preparingArg); + ReadWorkItems workItems = data.get(); + return preparing.apply(convertToWorkItemInfoList(filtering(workItems.get(), filtering), comparator.thenComparing(Comparator.reverseOrder())), workItems.getItemsPerSecond()); } catch (Exception e) { throw new InternalServerErrorException(e, request.getRequestURI()); } } - private static Map groupByClassNameCounting(List workItems, Comparator comparator) { + private static List filtering(List workItems, + Predicate filtering) { return workItems.stream() - .sorted(comparator) - .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.counting())); + .filter(filtering) + .collect(Collectors.toList()); } - private static Map groupByPriorityCounting(List workItems, Comparator comparator) { + private static List convertToWorkItemInfoList(List workItems, Comparator comparator) { return workItems.stream() .sorted(comparator) .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(a -> a.getPriority().name(), Collectors.counting())); + .collect(Collectors.toList()); } - private static Map groupByClassName(List workItems, Comparator comparator) { - return convertMap(workItems.stream() - .sorted(comparator) - .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.toList())), WorkItemInfoList::new); - } + static class GroupBy { + private GroupBy() {} - private static Map groupByClassNameMetrics(List workItems, Comparator comparator) { - return convertMap(workItems.stream() - .sorted(comparator) - .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.toList())), a -> new WorkItemInfoList(a).onlyMetrics()); - } + private static Map classNameCounting(List workItems) { + return sortMap(workItems.stream() + .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.counting())), Map.Entry.comparingByValue()); + } - private static Map groupByPriority(List workItems, Comparator comparator) { - return convertMap(workItems.stream() - .sorted(comparator) - .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(a -> a.getPriority().name(), Collectors.toList())), WorkItemInfoList::new); - } + private static Map priorityCounting(List workItems) { + return sortMap(workItems.stream() + .collect(Collectors.groupingBy(a -> a.getPriority().name(), Collectors.counting())), Map.Entry.comparingByValue()); + } - private static Map groupByPriorityMetrics(List workItems, Comparator comparator) { - return convertMap(workItems.stream() - .sorted(comparator) - .map(WorkItemInfo::new) - .collect(Collectors.groupingBy(a -> a.getPriority().name(), Collectors.toList())), a -> new WorkItemInfoList(a).onlyMetrics()); - } + private static Map className(List workItems, + ReadItemsPerSecond itemsPerSecond) { + return convertMap(workItems.stream().collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.toList())), + workItemExecutes -> new WorkItemInfoList(workItemExecutes, itemsPerSecond), Comparator.comparing(a -> a.getValue().size())); + } - private static Map groupByClassNameCounting(List workItems) { - return groupByClassNameCounting(workItems, Comparator.reverseOrder()); - } + private static Map classNameMetrics(List workItems, + ReadItemsPerSecond itemsPerSecond) { + return convertMap(workItems.stream() + .collect(Collectors.groupingBy(WorkItemInfo::getClassName, Collectors.toList())), + workItemExecutes -> new WorkItemInfoList(workItemExecutes, itemsPerSecond).onlyMetrics(), Comparator.comparing(a -> a.getValue().size())); + } - private static Map groupByClassName(List workItems) { - return groupByClassName(workItems, Comparator.reverseOrder()); - } + private static Map priority(List workItems, + ReadItemsPerSecond itemsPerSecond) { + return convertMap(workItems.stream() + .collect(Collectors.groupingBy(a -> a.getPriority().name(), Collectors.toList())), + workItemExecutes -> new WorkItemInfoList(workItemExecutes, itemsPerSecond), Comparator.comparing(a -> a.getValue().size())); + } - private static Map groupByClassNameMetrics(List workItems) { - return groupByClassNameMetrics(workItems, Comparator.reverseOrder()); + private static Map priorityMetrics(List workItems, + ReadItemsPerSecond itemsPerSecond) { + return convertMap(workItems.stream() + .collect(Collectors.groupingBy(a -> a.getPriority().name(), Collectors.toList())), + workItemExecutes -> new WorkItemInfoList(workItemExecutes, itemsPerSecond).onlyMetrics(), Comparator.comparing(a -> a.getValue().size())); + } } + static class Comparators { - private static Map groupByPriorityCounting(List workItems) { - return groupByPriorityCounting(workItems, Comparator.reverseOrder()); - } + private Comparators() {} - private static Map groupByPriority(List workItems) { - return groupByPriority(workItems, Comparator.reverseOrder()); - } + static Comparator byExecuteMsComparator() { + return (a, b) -> Long.compare(b.getWorkItem().getExecutedMs(), a.getWorkItem().getExecutedMs()); + } - private static Map groupByPriorityMetrics(List workItems) { - return groupByPriorityMetrics(workItems, Comparator.reverseOrder()); + static Comparator bySerialComparator() { + return (a, b) -> Long.compare(b.getSerial(), a.getSerial()); + } } - private static List getWorkItems(List workItems, Comparator comparator) { - return workItems.stream() - .sorted(comparator) - .map(WorkItemInfo::new) - .collect(Collectors.toList()); - } + static class Conditions { - private static List getWorkItems(List workItems) { - return getWorkItems(workItems, Comparator.reverseOrder()); - } + private Conditions() {} - private static Comparator byExecuteMsComparator() { - return (a, b) -> Long.compare(b.getWorkItem().getExecutedMs(), a.getWorkItem().getExecutedMs()); - } + static Predicate isExecuted() { + return execute -> execute.getWorkItem().isExecuted(); + } - private static Comparator bySerialComparator() { - return (a, b) -> Long.compare(b.getSerial(), a.getSerial()); - } + static Predicate isNotExecuted() { + return execute -> !execute.getWorkItem().isExecuted(); + } - private static List getWorkItems(HttpServletRequest request, WorkItemPriority priority) { - switch (priority) { - case HIGH: - return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryHighPriority); - case MEDIUM: - return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryMediumPriority); - case LOW: - return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryLowPriority); - case IDLE: - return get(request, WorkItemInfoApiService::getWorkItems, WorkItemsUtils::getHistoryIdle); - default: - return Collections.emptyList(); + static Predicate isSuccess() { + return execute -> execute.getWorkItem().isSuccess(); + } + + static Predicate isLessThan(long executedMs) { + return execute -> execute.getWorkItem().getExecutedMs() < executedMs; + } + + static Predicate isLongerThan(long executedMs) { + return execute -> execute.getWorkItem().getExecutedMs() > executedMs; + } + + static Predicate isRunning() { + return execute -> execute.getWorkItem().isRunning(); + } + + static Predicate isPriority(WorkItemPriority priority) { + return execute -> WorkItemPriority.priorityOf(execute.getWorkItem().getPriority()) == priority; } } } diff --git a/src/org/scada_lts/web/mvc/api/WorkItemInfoSecureAPI.java b/src/org/scada_lts/web/mvc/api/WorkItemInfoSecureAPI.java index 6139175d51..5776026dc7 100644 --- a/src/org/scada_lts/web/mvc/api/WorkItemInfoSecureAPI.java +++ b/src/org/scada_lts/web/mvc/api/WorkItemInfoSecureAPI.java @@ -15,7 +15,7 @@ import java.util.Map; @RestController -@RequestMapping(path = "/api/work-items-secure") +@RequestMapping(path = "/api/secure/work-items") public class WorkItemInfoSecureAPI { private final WorkItemInfoAPI workItemInfoAPI; @@ -34,6 +34,36 @@ public ResponseEntity getCurrentWorkItemsMetrics(HttpServletRe return workItemInfoAPI.getCurrentWorkItemsMetrics(request); } + @GetMapping(value = "/priority/{priority}") + public ResponseEntity getCurrentWorkItemsByPriority(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + return workItemInfoAPI.getCurrentWorkItemsByPriority(request, priority); + } + + @GetMapping(value = "/priority/{priority}/metrics") + public ResponseEntity getCurrentWorkItemsByPriorityMetrics(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + return workItemInfoAPI.getCurrentWorkItemsByPriorityMetrics(request, priority); + } + + @GetMapping(value = "/priority/{priority}/group-by/classes") + public ResponseEntity> getCurrentWorkItemsByPriorityGroupByClassName(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + return workItemInfoAPI.getCurrentWorkItemsByPriorityGroupByClassName(request, priority); + } + + @GetMapping(value = "/priority/{priority}/group-by/classes/metrics") + public ResponseEntity> getCurrentWorkItemsByPriorityGroupByClassNameMetrics(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + return workItemInfoAPI.getCurrentWorkItemsByPriorityGroupByClassNameMetrics(request, priority); + } + + @GetMapping(value = "/priority/{priority}/group-by/classes/count") + public ResponseEntity> getCurrentWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, + @PathVariable("priority") WorkItemPriority priority) { + return workItemInfoAPI.getCurrentWorkItemsByPriorityGroupByClassNameCount(request, priority); + } + @GetMapping(value = "/group-by/classes") public ResponseEntity> getCurrentWorkItemsGroupByClassName(HttpServletRequest request) { return workItemInfoAPI.getCurrentWorkItemsGroupByClassName(request); @@ -229,7 +259,7 @@ public ResponseEntity> getCurrentExecutedLongerWor @GetMapping(value = "/executed/longer/{executedMs}/group-by/priority/count") public ResponseEntity> getCurrentExecutedLongerWorkItemsGroupByPriorityCount(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getCurrentExecutedLongerWorkItemsGroupByPriorityCount(request, executedMs); } @@ -309,7 +339,7 @@ public ResponseEntity> getCurrentExecutedWorkItems @GetMapping(value = "/executed/priority/{priority}/group-by/classes/count") public ResponseEntity> getCurrentExecutedWorkItemsByPriorityGroupByClassNameCount(HttpServletRequest request, - @PathVariable("priority") WorkItemPriority priority) { + @PathVariable("priority") WorkItemPriority priority) { return workItemInfoAPI.getCurrentExecutedWorkItemsByPriorityGroupByClassNameCount(request, priority); } @@ -441,37 +471,37 @@ public ResponseEntity getHistoryExecutedLongerWorkItems(HttpSe @GetMapping(value = "/history/longer/{executedMs}/metrics") public ResponseEntity getHistoryExecutedLongerWorkItemsMetrics(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getHistoryExecutedLongerWorkItemsMetrics(request, executedMs); } @GetMapping(value = "/history/longer/{executedMs}/group-by/classes") public ResponseEntity> getHistoryExecutedLongerWorkItemsGroupByClassName(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getHistoryExecutedLongerWorkItemsGroupByClassName(request, executedMs); } @GetMapping(value = "/history/longer/{executedMs}/group-by/classes/metrics") public ResponseEntity> getHistoryExecutedLongerWorkItemsGroupByClassNameMetrics(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getHistoryExecutedLongerWorkItemsGroupByClassNameMetrics(request, executedMs); } @GetMapping(value = "/history/longer/{executedMs}/group-by/classes/count") public ResponseEntity> getHistoryExecutedLongerWorkItemsGroupByClassNameCount(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getHistoryExecutedLongerWorkItemsGroupByClassNameCount(request, executedMs); } @GetMapping(value = "/history/longer/{executedMs}/group-by/priority") public ResponseEntity> getHistoryExecutedLongerWorkItemsGroupByPriority(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getHistoryExecutedLongerWorkItemsGroupByPriority(request, executedMs); } @GetMapping(value = "/history/longer/{executedMs}/group-by/priority/metrics") public ResponseEntity> getHistoryExecutedLongerWorkItemsGroupByPriorityMetrics(HttpServletRequest request, - @PathVariable("executedMs") int executedMs) { + @PathVariable("executedMs") int executedMs) { return workItemInfoAPI.getHistoryExecutedLongerWorkItemsGroupByPriorityMetrics(request, executedMs); } @@ -552,7 +582,7 @@ public ResponseEntity> getHistoryProcessWorkItemsGroupByPriori } @RestController - @RequestMapping(path = "/api/work-items-secure/scheduled") + @RequestMapping(path = "/api/secure/work-items/scheduled") public static class ScheduledWorkItemInfoSecureAPI { private final WorkItemInfoAPI.ScheduledWorkItemInfoAPI scheduledWorkItemInfoAPI; diff --git a/src/org/scada_lts/web/mvc/api/json/JsonSettingsMisc.java b/src/org/scada_lts/web/mvc/api/json/JsonSettingsMisc.java index 536a9da249..cc3ab851cb 100644 --- a/src/org/scada_lts/web/mvc/api/json/JsonSettingsMisc.java +++ b/src/org/scada_lts/web/mvc/api/json/JsonSettingsMisc.java @@ -10,6 +10,10 @@ public class JsonSettingsMisc implements Serializable { public boolean hideShortcutDisableFullScreen; public int eventPendingLimit; public boolean eventPendingCacheEnabled; + public boolean workItemsReportingEnabled; + public boolean workItemsReportingItemsPerSecondEnabled; + public int workItemsReportingItemsPerSecondLimit; + public int threadsNameAdditionalLength; public JsonSettingsMisc() {} @@ -69,4 +73,36 @@ public boolean isEventPendingCacheEnabled() { public void setEventPendingCacheEnabled(boolean eventPendingCacheEnabled) { this.eventPendingCacheEnabled = eventPendingCacheEnabled; } + + public boolean isWorkItemsReportingEnabled() { + return workItemsReportingEnabled; + } + + public void setWorkItemsReportingEnabled(boolean workItemsReportingEnabled) { + this.workItemsReportingEnabled = workItemsReportingEnabled; + } + + public boolean isWorkItemsReportingItemsPerSecondEnabled() { + return workItemsReportingItemsPerSecondEnabled; + } + + public void setWorkItemsReportingItemsPerSecondEnabled(boolean workItemsReportingItemsPerSecondEnabled) { + this.workItemsReportingItemsPerSecondEnabled = workItemsReportingItemsPerSecondEnabled; + } + + public int getWorkItemsReportingItemsPerSecondLimit() { + return workItemsReportingItemsPerSecondLimit; + } + + public void setWorkItemsReportingItemsPerSecondLimit(int workItemsReportingItemsPerSecondLimit) { + this.workItemsReportingItemsPerSecondLimit = workItemsReportingItemsPerSecondLimit; + } + + public int getThreadsNameAdditionalLength() { + return threadsNameAdditionalLength; + } + + public void setThreadsNameAdditionalLength(int threadsNameAdditionalLength) { + this.threadsNameAdditionalLength = threadsNameAdditionalLength; + } } diff --git a/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java b/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java index 8622dba152..68529ac058 100644 --- a/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java +++ b/src/org/scada_lts/web/mvc/api/json/WorkItemInfo.java @@ -26,17 +26,5 @@ public String getClassName() { public WorkItemPriority getPriority() { return workItem.getPriority(); } - public long getItemsPerSecondOneSecond() { - return workItem.getItemsPerSecond(); - } - public long getItemsPerSecondOneMinute() { - return workItem.getItemsPerSecondOneMinute(); - } - public long getItemsPerSecondFiveMinutes() { - return workItem.getItemsPerSecondFiveMinutes(); - } - public long getItemsPerSecondFifteenMinutes() { - return workItem.getItemsPerSecondFifteenMinutes(); - } } diff --git a/src/org/scada_lts/web/mvc/api/json/WorkItemInfoList.java b/src/org/scada_lts/web/mvc/api/json/WorkItemInfoList.java index 16f30b75c7..b5ecefb802 100644 --- a/src/org/scada_lts/web/mvc/api/json/WorkItemInfoList.java +++ b/src/org/scada_lts/web/mvc/api/json/WorkItemInfoList.java @@ -2,6 +2,7 @@ import com.serotonin.mango.rt.maint.work.WorkItem; import com.serotonin.mango.rt.maint.work.WorkItemMetrics; +import org.scada_lts.quartz.ReadItemsPerSecond; import java.math.BigDecimal; import java.math.RoundingMode; @@ -11,25 +12,25 @@ import java.util.function.ToLongFunction; import java.util.stream.LongStream; -public class WorkItemInfoList { +import static org.scada_lts.utils.TimeUtils.toMs; +public class WorkItemInfoList { private final int size; private final long minExecutedMs; private final long avgExecutedMs; private final long maxExecutedMs; - - private final long minTimeInitMs; - private final long avgTimeInitMs; - private final long maxTimeInitMs; - private final long minExecutedNanos; private final long avgExecutedNanos; private final long maxExecutedNanos; + private final long minTimeInitMs; + private final long avgTimeInitMs; + private final long maxTimeInitMs; private final long minTimeInitNanos; private final long avgTimeInitNanos; private final long maxTimeInitNanos; + private final long itemsPerSecond; private final long itemsPerSecondOneMinute; private final long itemsPerSecondFiveMinutes; @@ -63,30 +64,37 @@ private WorkItemInfoList(WorkItemInfoList workItemInfoList, boolean onlyMetrics) this.workItemExecutes = onlyMetrics ? new ArrayList<>() : workItemInfoList.workItemExecutes; } - public WorkItemInfoList(List workItemExecutes) { - this.workItemExecutes = workItemExecutes; - this.size = workItemExecutes.size(); - - this.minExecutedMs = min(longStream(workItemExecutes, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedMs)); - this.avgExecutedMs = avg(longStream(workItemExecutes, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedMs)); - this.maxExecutedMs = max(longStream(workItemExecutes, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedMs)); - - this.minExecutedNanos = min(longStream(workItemExecutes, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedNanos)); - this.avgExecutedNanos = avg(longStream(workItemExecutes, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedNanos)); - this.maxExecutedNanos = max(longStream(workItemExecutes, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedNanos)); - - this.minTimeInitMs = min(longStream(workItemExecutes, workItem -> workItem.getTimeInitMs() != -1, WorkItemMetrics::getTimeInitMs)); - this.avgTimeInitMs = avg(longStream(workItemExecutes, workItem -> workItem.getTimeInitMs() != -1, WorkItemMetrics::getTimeInitMs)); - this.maxTimeInitMs = max(longStream(workItemExecutes, workItem -> workItem.getTimeInitMs() != -1, WorkItemMetrics::getTimeInitMs)); - - this.minTimeInitNanos = min(longStream(workItemExecutes, workItem -> workItem.getTimeInitNanos() != -1, WorkItemMetrics::getTimeInitNanos)); - this.avgTimeInitNanos = avg(longStream(workItemExecutes, workItem -> workItem.getTimeInitNanos() != -1, WorkItemMetrics::getTimeInitNanos)); - this.maxTimeInitNanos = max(longStream(workItemExecutes, workItem -> workItem.getTimeInitNanos() != -1, WorkItemMetrics::getTimeInitNanos)); - - this.itemsPerSecond = avg(workItemExecutes.stream().filter(a -> a.getItemsPerSecondOneSecond() > -1).mapToLong(WorkItemInfo::getItemsPerSecondOneSecond)); - this.itemsPerSecondOneMinute = avg(workItemExecutes.stream().filter(a -> a.getItemsPerSecondOneMinute() > -1).mapToLong(WorkItemInfo::getItemsPerSecondOneMinute)); - this.itemsPerSecondFiveMinutes = avg(workItemExecutes.stream().filter(a -> a.getItemsPerSecondFiveMinutes() > -1).mapToLong(WorkItemInfo::getItemsPerSecondFiveMinutes)); - this.itemsPerSecondFifteenMinutes = avg(workItemExecutes.stream().filter(a -> a.getItemsPerSecondFifteenMinutes() > -1).mapToLong(WorkItemInfo::getItemsPerSecondFifteenMinutes)); + public WorkItemInfoList(List workItemInfoList, ReadItemsPerSecond itemsPerSecond) { + this.workItemExecutes = workItemInfoList; + this.size = workItemInfoList.size(); + + this.minExecutedNanos = min(longStream(workItemInfoList, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedNanos)); + this.avgExecutedNanos = avg(longStream(workItemInfoList, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedNanos)); + this.maxExecutedNanos = max(longStream(workItemInfoList, WorkItemMetrics::isExecuted, WorkItemMetrics::getExecutedNanos)); + + this.minExecutedMs = toMs(minExecutedNanos); + this.avgExecutedMs = toMs(avgExecutedNanos); + this.maxExecutedMs = toMs(maxExecutedNanos); + + this.minTimeInitNanos = min(longStream(workItemInfoList, workItem -> workItem.getTimeInitNanos() > -1, WorkItemMetrics::getTimeInitNanos)); + this.avgTimeInitNanos = avg(longStream(workItemInfoList, workItem -> workItem.getTimeInitNanos() > -1, WorkItemMetrics::getTimeInitNanos)); + this.maxTimeInitNanos = max(longStream(workItemInfoList, workItem -> workItem.getTimeInitNanos() > -1, WorkItemMetrics::getTimeInitNanos)); + + this.minTimeInitMs = toMs(minTimeInitNanos); + this.avgTimeInitMs = toMs(avgTimeInitNanos); + this.maxTimeInitMs = toMs(maxTimeInitNanos); + + if(itemsPerSecond != null) { + this.itemsPerSecond = itemsPerSecond.itemsPerSecond(); + this.itemsPerSecondOneMinute = itemsPerSecond.itemsPerSecondFromOneMinute(); + this.itemsPerSecondFiveMinutes = itemsPerSecond.itemsPerSecondFromFiveMinutes(); + this.itemsPerSecondFifteenMinutes = itemsPerSecond.itemsPerSecondFromFifteenMinutes(); + } else { + this.itemsPerSecond = -1; + this.itemsPerSecondOneMinute = -1; + this.itemsPerSecondFiveMinutes = -1; + this.itemsPerSecondFifteenMinutes = -1; + } } public List getWorkItemExecutes() { diff --git a/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java b/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java index 68b7420e16..657e4b7f3d 100644 --- a/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java +++ b/test/com/serotonin/mango/rt/maint/work/CreateWorkItemToStringTest.java @@ -1,9 +1,31 @@ package com.serotonin.mango.rt.maint.work; +import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.scada_lts.mango.service.SystemSettingsService; +import static org.powermock.api.mockito.PowerMockito.mock; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({SystemSettingsService.class}) +// resources/org/powermock/extensions/configuration.properties is not working +@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*", "com.sun.org.apache.xalan.*", + "javax.activation.*", "javax.management.*"}) public class CreateWorkItemToStringTest { + @BeforeClass + public static void config() throws Exception { + SystemSettingsService systemSettingsService = mock(SystemSettingsService.class); + PowerMockito.whenNew(SystemSettingsService.class) + .withNoArguments() + .thenReturn(systemSettingsService); + } + @Test public void when_EmailFinallyWorkItem_newInstance() { //when: diff --git a/test/env.properties b/test/env.properties index c924f02079..efb9db33a0 100644 --- a/test/env.properties +++ b/test/env.properties @@ -137,4 +137,7 @@ view.hideShortcutDisableFullScreen=false eventdetector.cache.enabled=true event.pending.limit=101 event.pending.update.limit=5001 -thread.name.additional.length=5 \ No newline at end of file +threads.name.additional.length=5 +workitems.reporting.enabled=false +workitems.reporting.itemspersecond.enabled=false +workitems.reporting.itemspersecond.limit=20000 \ No newline at end of file diff --git a/test/org/scada_lts/utils/ThreadUtilsTest.java b/test/org/scada_lts/utils/ThreadUtilsTest.java index 4c4ae6b476..7354f6d5f2 100644 --- a/test/org/scada_lts/utils/ThreadUtilsTest.java +++ b/test/org/scada_lts/utils/ThreadUtilsTest.java @@ -1,17 +1,30 @@ package org.scada_lts.utils; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.scada_lts.mango.service.SystemSettingsService; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ThreadUtilsTest { + private SystemSettingsService systemSettingsServiceMock; + + @Before + public void config() { + this.systemSettingsServiceMock = mock(SystemSettingsService.class); + when(systemSettingsServiceMock.getThreadsNameAdditionalLength()).thenReturn(5); + } + @Test public void when_reduceName_for_abc12345_and_limit_5_then_abc12_() { //given: String expected = "abc12.."; //when: - String result = ThreadUtils.reduceName("abc12345"); + String result = ThreadUtils.reduceName("abc12345", systemSettingsServiceMock); //then: Assert.assertEquals(expected, result); @@ -23,7 +36,7 @@ public void when_reduceName_for_abc123_and_limit_5_then_abc12_() { String expected = "abc12.."; //when: - String result = ThreadUtils.reduceName("abc123"); + String result = ThreadUtils.reduceName("abc123", systemSettingsServiceMock); //then: Assert.assertEquals(expected, result); @@ -35,7 +48,7 @@ public void when_reduceName_for_abc12_and_limit_5_then_abc12() { String expected = "abc12"; //when: - String result = ThreadUtils.reduceName("abc12"); + String result = ThreadUtils.reduceName("abc12", systemSettingsServiceMock); //then: Assert.assertEquals(expected, result); @@ -47,7 +60,7 @@ public void when_reduceName_for_abc1_and_limit_5_then_abc1() { String expected = "abc1"; //when: - String result = ThreadUtils.reduceName("abc1"); + String result = ThreadUtils.reduceName("abc1", systemSettingsServiceMock); //then: Assert.assertEquals(expected, result); diff --git a/test/org/scada_lts/web/mvc/api/json/WorkItemInfoListTest.java b/test/org/scada_lts/web/mvc/api/json/WorkItemInfoListTest.java new file mode 100644 index 0000000000..4d14941ae6 --- /dev/null +++ b/test/org/scada_lts/web/mvc/api/json/WorkItemInfoListTest.java @@ -0,0 +1,116 @@ +package org.scada_lts.web.mvc.api.json; + +import com.serotonin.mango.rt.maint.work.*; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.scada_lts.mango.service.SystemSettingsService; +import org.scada_lts.quartz.ItemsPerSecond; + +import java.util.stream.Collectors; +import static org.scada_lts.utils.TimeUtils.toMs; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({SystemSettingsService.class, AbstractBeforeAfterWorkItem.class, ItemsPerSecond.class}) +// resources/org/powermock/extensions/configuration.properties is not working +@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*", "com.sun.org.apache.xalan.*", + "javax.activation.*", "javax.management.*"}) +public class WorkItemInfoListTest { + + private static WorkItemInfoList workItemInfoList; + + @BeforeClass + public static void config() throws Exception { + SystemSettingsService systemSettingsServiceMock = PowerMockito.mock(SystemSettingsService.class); + PowerMockito.when(systemSettingsServiceMock.getThreadsNameAdditionalLength()).thenReturn(255); + PowerMockito.when(systemSettingsServiceMock.isWorkItemsReportingEnabled()).thenReturn(true); + PowerMockito.when(systemSettingsServiceMock.isWorkItemsReportingItemsPerSecondEnabled()).thenReturn(false); + PowerMockito.when(systemSettingsServiceMock.getWorkItemsReportingItemsPerSecondLimit()).thenReturn(0); + PowerMockito.whenNew(SystemSettingsService.class) + .withNoArguments() + .thenReturn(systemSettingsServiceMock); + + //given: + for(int i=0; i < 50; i++) { + WorkItem workItem = new AbstractBeforeAfterWorkItem() { + @Override + public void work() { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public int getPriority() { + return 0; + } + + @Override + public String getDetails() { + return ""; + } + }; + workItem.execute(); + } + //when: + ReadWorkItems workItems = WorkItemsUtils.getCurrentAll(); + workItemInfoList = new WorkItemInfoList(workItems.get().stream().map(WorkItemInfo::new).collect(Collectors.toList()), workItems.getItemsPerSecond()); + } + + @Test + public void when_getSize_then_50() { + //then: + Assert.assertEquals(50, workItemInfoList.getSize()); + } + + @Test + public void when_getAvgExecutedMs_and_getAvgExecutedNanos_and_convert_to_ms_then_equals() { + + //when: + long ms = workItemInfoList.getAvgExecutedMs(); + long nanos = workItemInfoList.getAvgExecutedNanos(); + + //then: + Assert.assertEquals(ms, toMs(nanos)); + } + + @Test + public void when_getMinExecutedMs_and_getMinExecutedNanos_and_convert_to_ms_then_equals() { + + //when: + long ms = workItemInfoList.getMinExecutedMs(); + long nanos = workItemInfoList.getMinExecutedNanos(); + + //then: + Assert.assertEquals(ms, toMs(nanos)); + } + + @Test + public void when_getMaxExecutedMs_and_getMaxExecutedNanos_and_convert_to_ms_then_equals() { + + //when: + long ms = workItemInfoList.getMaxExecutedMs(); + long nanos = workItemInfoList.getMaxExecutedNanos(); + + //then: + Assert.assertEquals(ms, toMs(nanos)); + } + + @Test + public void when_getAvgTimeInitMs_and_getAvgTimeInitNanos_and_convert_to_ms_then_equals() { + + //when: + long ms = workItemInfoList.getAvgTimeInitMs(); + long nanos = workItemInfoList.getAvgTimeInitNanos(); + + //then: + Assert.assertEquals(ms, toMs(nanos)); + } +} \ No newline at end of file diff --git a/webapp-resources/env.properties b/webapp-resources/env.properties index 8a65ecba0d..d639defaa0 100644 --- a/webapp-resources/env.properties +++ b/webapp-resources/env.properties @@ -116,10 +116,10 @@ comm.serial.timeout.mode=0 comm.serial.sleep=1000 systemsettings.email.timeout=60000 -processing.workitems.limit=500 +processing.workitems.limit=1000 processing.workitems.running.limit=51 processing.workitems.running.repeat=51 -processing.workitems.history.limit=500 +processing.workitems.history.limit=1000 processing.workitems.history.failed.limit=200 processing.workitems.history.process.limit=200 processing.workitems.history.priority.high.limit=200 @@ -138,7 +138,7 @@ view.hideShortcutDisableFullScreen=false eventdetector.cache.enabled=true event.pending.limit=101 event.pending.update.limit=5001 -thread.name.additional.length=255 +threads.name.additional.length=255 workitems.reporting.enabled=false workitems.reporting.itemspersecond.enabled=false workitems.reporting.itemspersecond.limit=20000 \ No newline at end of file diff --git a/webapp-resources/messages_de.properties b/webapp-resources/messages_de.properties index a48daa3759..374baa11b5 100644 --- a/webapp-resources/messages_de.properties +++ b/webapp-resources/messages_de.properties @@ -3325,4 +3325,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_en.properties b/webapp-resources/messages_en.properties index 9bbdbb9bff..d6a78391ee 100644 --- a/webapp-resources/messages_en.properties +++ b/webapp-resources/messages_en.properties @@ -3328,4 +3328,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_es.properties b/webapp-resources/messages_es.properties index 7269d85d03..1f97af2a86 100644 --- a/webapp-resources/messages_es.properties +++ b/webapp-resources/messages_es.properties @@ -3368,4 +3368,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_fi.properties b/webapp-resources/messages_fi.properties index 425045fd34..de900d9a80 100644 --- a/webapp-resources/messages_fi.properties +++ b/webapp-resources/messages_fi.properties @@ -3454,4 +3454,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_fr.properties b/webapp-resources/messages_fr.properties index 526eb4e317..1ce66879db 100644 --- a/webapp-resources/messages_fr.properties +++ b/webapp-resources/messages_fr.properties @@ -3322,4 +3322,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_lu.properties b/webapp-resources/messages_lu.properties index 5c5a00fb8d..ed0b8b21b1 100644 --- a/webapp-resources/messages_lu.properties +++ b/webapp-resources/messages_lu.properties @@ -3342,3 +3342,7 @@ systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_nl.properties b/webapp-resources/messages_nl.properties index 71142ab07f..9c4196f8fb 100644 --- a/webapp-resources/messages_nl.properties +++ b/webapp-resources/messages_nl.properties @@ -3444,4 +3444,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_pl.properties b/webapp-resources/messages_pl.properties index 1aa5d95ead..1812452144 100644 --- a/webapp-resources/messages_pl.properties +++ b/webapp-resources/messages_pl.properties @@ -3466,4 +3466,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_pt.properties b/webapp-resources/messages_pt.properties index 09d31e84bd..6eab1de874 100644 --- a/webapp-resources/messages_pt.properties +++ b/webapp-resources/messages_pt.properties @@ -3480,4 +3480,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_ru.properties b/webapp-resources/messages_ru.properties index 03e7ddbec6..5fbcfd8713 100644 --- a/webapp-resources/messages_ru.properties +++ b/webapp-resources/messages_ru.properties @@ -3476,4 +3476,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknown -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file diff --git a/webapp-resources/messages_zh.properties b/webapp-resources/messages_zh.properties index ba822cbe59..e0cd187f97 100644 --- a/webapp-resources/messages_zh.properties +++ b/webapp-resources/messages_zh.properties @@ -3429,4 +3429,8 @@ validate.1to255=Must be between 1 and 255 inclusive systemsettings.event.pendingLimit=Event Pending Limit systemsettings.event.pendingCacheEnabled=Enabled Event Pending Cache annotation.unknown=Unknow -event.auto.acknowledge=Automatic acknowledge \ No newline at end of file +event.auto.acknowledge=Automatic acknowledge +systemsettings.workitems.reporting.enabled=Work items reporting enabled +systemsettings.workitems.reporting.itemspersecond.enabled=Items per second reporting enabled +systemsettings.workitems.reporting.itemspersecond.limit=Items per second reporting limit +systemsettings.threads.name.additional.length=Thread name length \ No newline at end of file From 4bc25b764faf2fc30fe1860ff15feda9a714fd77 Mon Sep 17 00:00:00 2001 From: Kamil Jarmusik Date: Wed, 25 Oct 2023 14:25:24 +0200 Subject: [PATCH 43/43] #2694 Work-items API extension - revert default update event type on 'Context update'; revert AbstractBeforeAfterWorkItem.workFinally no logging error --- .../mango/rt/maint/work/AbstractBeforeAfterWorkItem.java | 5 +---- .../mango/vo/dataSource/meta/MetaPointLocatorVO.java | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java index 5bf29b77c1..fb3dc1e424 100644 --- a/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java +++ b/src/com/serotonin/mango/rt/maint/work/AbstractBeforeAfterWorkItem.java @@ -237,10 +237,7 @@ public void workSuccessFail(Throwable exception) { } @Override - public void workFinally(Map exceptions) { - if(!exceptions.isEmpty()) - LOG.error(exceptionsToString(exceptions) + " - " + this, exceptions.entrySet().iterator().next().getValue()); - } + public void workFinally(Map exceptions) {} @Override public void workFinallyFail(Throwable finallyException, Map exceptions) { diff --git a/src/com/serotonin/mango/vo/dataSource/meta/MetaPointLocatorVO.java b/src/com/serotonin/mango/vo/dataSource/meta/MetaPointLocatorVO.java index 62fb2c0a46..ddcbb0f948 100644 --- a/src/com/serotonin/mango/vo/dataSource/meta/MetaPointLocatorVO.java +++ b/src/com/serotonin/mango/vo/dataSource/meta/MetaPointLocatorVO.java @@ -81,7 +81,7 @@ public class MetaPointLocatorVO extends AbstractPointLocatorVO implements JsonSe private int dataTypeId; @JsonRemoteProperty private boolean settable; - private int updateEvent = UPDATE_EVENT_CONTEXT_CHANGE; + private int updateEvent = UPDATE_EVENT_CONTEXT_UPDATE; @JsonRemoteProperty private String updateCronPattern; @JsonRemoteProperty