mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-01 02:38:21 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
2ec32860de
@ -144,6 +144,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -711,6 +712,8 @@ create table SYS_CATEGORY_ATTR(
|
||||
REQUIRED boolean,
|
||||
LOOKUP boolean,
|
||||
TARGET_SCREENS varchar(4000),
|
||||
WIDTH varchar(20),
|
||||
ROWS_COUNT integer,
|
||||
--
|
||||
primary key (ID)
|
||||
)^
|
||||
|
@ -142,6 +142,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
create clustered index IDX_SYS_SCHEDULED_EXECUTION_CREATE_TS on SYS_SCHEDULED_EXECUTION (CREATE_TS)^
|
||||
|
||||
@ -754,6 +755,8 @@ create table SYS_CATEGORY_ATTR (
|
||||
REQUIRED tinyint,
|
||||
LOOKUP tinyint,
|
||||
TARGET_SCREENS varchar(4000),
|
||||
WIDTH varchar(20),
|
||||
ROWS_COUNT integer,
|
||||
--
|
||||
primary key nonclustered (ID),
|
||||
constraint SYS_CATEGORY_ATTR_CATEGORY_ID foreign key (CATEGORY_ID) references SYS_CATEGORY(ID)
|
||||
|
@ -141,6 +141,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
/**********************************************************************************************/
|
||||
|
||||
@ -759,6 +760,8 @@ create table SYS_CATEGORY_ATTR (
|
||||
REQUIRED boolean,
|
||||
LOOKUP boolean,
|
||||
TARGET_SCREENS varchar(4000),
|
||||
WIDTH varchar(20),
|
||||
ROWS_COUNT integer,
|
||||
--
|
||||
primary key (ID),
|
||||
constraint SYS_CATEGORY_ATTR_CATEGORY_ID foreign key (CATEGORY_ID) references SYS_CATEGORY(ID)
|
||||
|
@ -77,6 +77,8 @@ create table SYS_CATEGORY_ATTR (
|
||||
REQUIRED char(1),
|
||||
LOOKUP char(1),
|
||||
TARGET_SCREENS varchar2(4000),
|
||||
WIDTH varchar2(20),
|
||||
ROWS_COUNT integer,
|
||||
primary key(ID)
|
||||
)^
|
||||
create index IDX_SYS_CATEGORY_ATTR_CATEGORY on SYS_CATEGORY_ATTR(CATEGORY_ID)^
|
||||
@ -233,6 +235,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
primary key(ID)
|
||||
)^
|
||||
create index IDX_SYS_SCH_EXE_TAS_STA_TIM on SYS_SCHEDULED_EXECUTION(TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCH_EXE_TAS_FI_TIM on SYS_SCHEDULED_EXECUTION(TASK_ID, FINISH_TIME)^
|
||||
|
||||
create table SYS_SCHEDULED_TASK (
|
||||
ID varchar2(32) not null,
|
||||
|
@ -141,6 +141,7 @@ create table SYS_SCHEDULED_EXECUTION (
|
||||
)^
|
||||
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_START_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, START_TIME)^
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
||||
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -723,6 +724,8 @@ create table SYS_CATEGORY_ATTR (
|
||||
REQUIRED boolean,
|
||||
LOOKUP boolean,
|
||||
TARGET_SCREENS varchar(4000),
|
||||
WIDTH varchar(20),
|
||||
ROWS_COUNT integer,
|
||||
--
|
||||
primary key (ID),
|
||||
constraint SYS_CATEGORY_ATTR_CATEGORY_ID foreign key (CATEGORY_ID) references SYS_CATEGORY(ID)
|
||||
|
@ -0,0 +1,2 @@
|
||||
alter table SYS_CATEGORY_ATTR add WIDTH varchar(20)^
|
||||
alter table SYS_CATEGORY_ATTR add ROWS_COUNT integer^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -0,0 +1,2 @@
|
||||
alter table SYS_CATEGORY_ATTR add WIDTH varchar(20)^
|
||||
alter table SYS_CATEGORY_ATTR add ROWS_COUNT integer^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -0,0 +1,2 @@
|
||||
alter table SYS_CATEGORY_ATTR add WIDTH varchar(20)^
|
||||
alter table SYS_CATEGORY_ATTR add ROWS_COUNT integer^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -0,0 +1,2 @@
|
||||
alter table SYS_CATEGORY_ATTR add WIDTH varchar2(20)^
|
||||
alter table SYS_CATEGORY_ATTR add ROWS_COUNT integer^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCH_EXE_TAS_FI_TIM on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)
|
@ -0,0 +1,2 @@
|
||||
alter table SYS_CATEGORY_ATTR add WIDTH varchar(20)^
|
||||
alter table SYS_CATEGORY_ATTR add ROWS_COUNT integer^
|
@ -0,0 +1 @@
|
||||
create index IDX_SYS_SCHEDULED_EXECUTION_TASK_FINISH_TIME on SYS_SCHEDULED_EXECUTION (TASK_ID, FINISH_TIME)^
|
@ -55,13 +55,12 @@ public interface PersistenceSecurity extends Security {
|
||||
/**
|
||||
* Applies in-memory constraints to the entity
|
||||
* @param entity -
|
||||
* @return true, if entity should be filtered from client output
|
||||
*/
|
||||
boolean applyConstraints(Entity entity);
|
||||
void applyConstraints(Entity entity);
|
||||
|
||||
/**
|
||||
* Applies in-memory constraints to the collection of entities and filter the collection
|
||||
* @param entities -
|
||||
* Applies in-memory constraints to the entity fields
|
||||
* @param entities - collection of entities
|
||||
*/
|
||||
void applyConstraints(Collection<Entity> entities);
|
||||
|
||||
@ -71,6 +70,13 @@ public interface PersistenceSecurity extends Security {
|
||||
*/
|
||||
boolean filterByConstraints(Collection<Entity> entities);
|
||||
|
||||
/**
|
||||
* Filter entities in collection by in-memory constraints
|
||||
* @param entity - collection of entities that will be filtered
|
||||
* @return true, if entity should be filtered from client output
|
||||
*/
|
||||
boolean filterByConstraints(Entity entity);
|
||||
|
||||
/**
|
||||
* Reads security token and restores filtered data
|
||||
* @param resultEntity -
|
||||
|
@ -98,6 +98,7 @@ public class DataManagerBean implements DataManager {
|
||||
}
|
||||
|
||||
E result = null;
|
||||
boolean needToApplyInMemoryConstraints = needToApplyInMemoryConstraints(context);
|
||||
try (Transaction tx = persistence.createTransaction()) {
|
||||
final EntityManager em = persistence.getEntityManager();
|
||||
|
||||
@ -115,8 +116,13 @@ public class DataManagerBean implements DataManager {
|
||||
|
||||
//noinspection unchecked
|
||||
List<E> resultList = executeQuery(query, singleResult);
|
||||
if (!resultList.isEmpty())
|
||||
if (!resultList.isEmpty()) {
|
||||
result = resultList.get(0);
|
||||
}
|
||||
|
||||
if (result != null && needToApplyInMemoryConstraints && security.filterByConstraints(result)) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
if (result instanceof BaseGenericIdEntity && context.isLoadDynamicAttributes()) {
|
||||
dynamicAttributesManagerAPI.fetchDynamicAttributes(Collections.singletonList((BaseGenericIdEntity) result));
|
||||
@ -125,12 +131,13 @@ public class DataManagerBean implements DataManager {
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
if (result != null && needToApplyConstraints(context) && security.applyConstraints(result)) {
|
||||
return null;
|
||||
if (result != null) {
|
||||
if (needToApplyInMemoryConstraints) {
|
||||
security.applyConstraints(result);
|
||||
}
|
||||
attributeSecurity.afterLoad(result);
|
||||
}
|
||||
|
||||
attributeSecurity.afterLoad(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -183,7 +190,7 @@ public class DataManagerBean implements DataManager {
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
if (needToApplyConstraints(context)) {
|
||||
if (needToApplyInMemoryConstraints(context)) {
|
||||
security.applyConstraints((Collection<Entity>) resultList);
|
||||
}
|
||||
|
||||
@ -208,7 +215,7 @@ public class DataManagerBean implements DataManager {
|
||||
|
||||
queryResultsManager.savePreviousQueryResults(context);
|
||||
|
||||
if (security.hasMemoryConstraints(metaClass, ConstraintOperationType.READ, ConstraintOperationType.ALL) ) {
|
||||
if (security.hasMemoryConstraints(metaClass, ConstraintOperationType.READ, ConstraintOperationType.ALL)) {
|
||||
context = context.copy();
|
||||
List resultList;
|
||||
try (Transaction tx = persistence.createTransaction()) {
|
||||
@ -381,6 +388,10 @@ public class DataManagerBean implements DataManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isAuthorizationRequired() && userSessionSource.getUserSession().hasConstraints()) {
|
||||
security.filterByConstraints(res);
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
@ -561,7 +572,7 @@ public class DataManagerBean implements DataManager {
|
||||
View view = context.getView() != null ? context.getView() :
|
||||
viewRepository.getView(metadata.getClassNN(context.getMetaClass()), View.LOCAL);
|
||||
View copy = View.copy(attributeSecurity.createRestrictedView(view));
|
||||
if (context.isLoadPartialEntities()) {
|
||||
if (context.isLoadPartialEntities() && !needToApplyInMemoryConstraints(context)) {
|
||||
copy.setLoadPartialEntities(true);
|
||||
}
|
||||
return copy;
|
||||
@ -570,16 +581,17 @@ public class DataManagerBean implements DataManager {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E extends Entity> List<E> getResultList(LoadContext<E> context, Query query, boolean ensureDistinct) {
|
||||
List<E> list = executeQuery(query, false);
|
||||
boolean filteredByConstraints = false;
|
||||
int initialSize = list.size();
|
||||
if (initialSize == 0) {
|
||||
return list;
|
||||
}
|
||||
if (needFilterByConstraints(context)) {
|
||||
boolean needApplyConstraints = needToApplyInMemoryConstraints(context);
|
||||
boolean filteredByConstraints = false;
|
||||
if (needApplyConstraints) {
|
||||
filteredByConstraints = security.filterByConstraints((Collection<Entity>) list);
|
||||
}
|
||||
if (!ensureDistinct) {
|
||||
return filteredByConstraints ? getResultListIteratively(context, query, list, initialSize) : list;
|
||||
return filteredByConstraints ? getResultListIteratively(context, query, list, initialSize, true) : list;
|
||||
}
|
||||
|
||||
int requestedFirst = context.getQuery().getFirstResult();
|
||||
@ -590,13 +602,13 @@ public class DataManagerBean implements DataManager {
|
||||
}
|
||||
// In case of not first chunk, even if there where no duplicates, start filling the set from zero
|
||||
// to ensure correct paging
|
||||
return getResultListIteratively(context, query, set, initialSize);
|
||||
return getResultListIteratively(context, query, set, initialSize, needApplyConstraints);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E extends Entity> List<E> getResultListIteratively(LoadContext<E> context, Query query,
|
||||
Collection<E> filteredCollection,
|
||||
int initialSize) {
|
||||
int initialSize, boolean needApplyConstraints) {
|
||||
int requestedFirst = context.getQuery().getFirstResult();
|
||||
int requestedMax = context.getQuery().getMaxResults();
|
||||
|
||||
@ -614,7 +626,7 @@ public class DataManagerBean implements DataManager {
|
||||
int maxResults = (requestedFirst + requestedMax) * factor;
|
||||
int i = 0;
|
||||
while (filteredCollection.size() < setSize) {
|
||||
if (i++ > 1000) {
|
||||
if (i++ > 10000) {
|
||||
log.warn("In-memory distinct: endless loop detected for " + context);
|
||||
break;
|
||||
}
|
||||
@ -625,7 +637,7 @@ public class DataManagerBean implements DataManager {
|
||||
if (list.size() == 0) {
|
||||
break;
|
||||
}
|
||||
if (needFilterByConstraints(context)) {
|
||||
if (needApplyConstraints) {
|
||||
security.filterByConstraints((Collection<Entity>) list);
|
||||
}
|
||||
filteredCollection.addAll(list);
|
||||
@ -796,33 +808,25 @@ public class DataManagerBean implements DataManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean needToApplyConstraints(LoadContext context) {
|
||||
protected boolean needToApplyInMemoryConstraints(LoadContext context) {
|
||||
if (!isAuthorizationRequired() || !userSessionSource.getUserSession().hasConstraints()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.getView() == null) {
|
||||
return false;
|
||||
MetaClass metaClass = metadata.getSession().getClassNN(context.getMetaClass());
|
||||
return security.hasMemoryConstraints(metaClass, ConstraintOperationType.READ, ConstraintOperationType.ALL);
|
||||
}
|
||||
|
||||
Session session = metadata.getSession();
|
||||
for (Class aClass : collectEntityClasses(context.getView(), new HashSet<>())) {
|
||||
if (security.hasConstraints(session.getClassNN(aClass))) {
|
||||
if (security.hasMemoryConstraints(session.getClassNN(aClass), ConstraintOperationType.READ, ConstraintOperationType.ALL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean needFilterByConstraints(LoadContext context) {
|
||||
if (!isAuthorizationRequired() || !userSessionSource.getUserSession().hasConstraints()) {
|
||||
return false;
|
||||
}
|
||||
MetaClass metaClass = metadata.getSession().getClassNN(context.getMetaClass());
|
||||
return security.hasConstraints(metaClass);
|
||||
}
|
||||
|
||||
|
||||
protected Set<Class> collectEntityClasses(View view, Set<View> visited) {
|
||||
if (visited.contains(view)) {
|
||||
return Collections.emptySet();
|
||||
|
@ -19,6 +19,7 @@ package com.haulmont.cuba.core.app.scheduling;
|
||||
|
||||
import com.haulmont.cuba.core.entity.ScheduledTask;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -40,4 +41,6 @@ public interface Coordinator {
|
||||
void end(Context context);
|
||||
|
||||
boolean isLastExecutionFinished(ScheduledTask task, long now);
|
||||
|
||||
long getLastFinished(ScheduledTask task);
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ import com.haulmont.cuba.core.Transaction;
|
||||
import com.haulmont.cuba.core.entity.ScheduledTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.LockModeType;
|
||||
import java.util.Date;
|
||||
@ -35,7 +35,6 @@ import java.util.List;
|
||||
* Implementation of {@link Coordinator} interface, performing synchronization of singleton schedulers on the main
|
||||
* database.
|
||||
* <p>This implementation should not be used if the database is overloaded.</p>
|
||||
*
|
||||
*/
|
||||
@Component(Coordinator.NAME)
|
||||
public class DbBasedCoordinator implements Coordinator {
|
||||
@ -108,6 +107,16 @@ public class DbBasedCoordinator implements Coordinator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastFinished(ScheduledTask task) {
|
||||
EntityManager em = persistence.getEntityManager();
|
||||
Query query = em.createQuery(
|
||||
"select max(e.finishTime) from sys$ScheduledExecution e where e.task.id = ?1")
|
||||
.setParameter(1, task.getId());
|
||||
Date date = (Date) query.getFirstResult();
|
||||
return date == null ? 0 : date.getTime();
|
||||
}
|
||||
|
||||
protected synchronized List<ScheduledTask> getTasks() {
|
||||
log.trace("Read all active tasks from DB and lock them");
|
||||
EntityManager em = persistence.getEntityManager();
|
||||
|
@ -25,6 +25,7 @@ import com.haulmont.cuba.core.app.ServerInfoAPI;
|
||||
import com.haulmont.cuba.core.app.scheduled.MethodParameterInfo;
|
||||
import com.haulmont.cuba.core.entity.ScheduledExecution;
|
||||
import com.haulmont.cuba.core.entity.ScheduledTask;
|
||||
import com.haulmont.cuba.core.entity.SchedulingType;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.core.global.Scripting;
|
||||
@ -104,33 +105,31 @@ public class RunnerBean implements Runner {
|
||||
// It's better not to pass an entity instance in managed state to another thread
|
||||
final ScheduledTask taskCopy = metadata.getTools().copy(task);
|
||||
|
||||
executorService.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug(taskCopy + ": running");
|
||||
try {
|
||||
boolean runConcurrent = scheduling.setRunning(taskCopy, true);
|
||||
if (!runConcurrent) {
|
||||
executorService.submit(() -> {
|
||||
log.debug("{}: running", taskCopy);
|
||||
try {
|
||||
boolean runConcurrent = scheduling.setRunning(taskCopy, true);
|
||||
if (!runConcurrent) {
|
||||
try {
|
||||
setSecurityContext(taskCopy, userSession);
|
||||
ScheduledExecution execution = registerExecutionStart(taskCopy, now);
|
||||
statisticsCounter.incCubaScheduledTasksCount();
|
||||
try {
|
||||
setSecurityContext(taskCopy, userSession);
|
||||
ScheduledExecution execution = registerExecutionStart(taskCopy, now);
|
||||
statisticsCounter.incCubaScheduledTasksCount();
|
||||
try {
|
||||
Object result = executeTask(taskCopy);
|
||||
registerExecutionFinish(taskCopy, execution, result);
|
||||
} catch (Throwable throwable) {
|
||||
registerExecutionFinish(taskCopy, execution, throwable);
|
||||
throw throwable;
|
||||
}
|
||||
} finally {
|
||||
scheduling.setRunning(taskCopy, false);
|
||||
Object result = executeTask(taskCopy);
|
||||
registerExecutionFinish(taskCopy, execution, result);
|
||||
} catch (Throwable throwable) {
|
||||
registerExecutionFinish(taskCopy, execution, throwable);
|
||||
throw throwable;
|
||||
}
|
||||
} else {
|
||||
log.info("Detected concurrent task execution: {}, skip it", taskCopy);
|
||||
} finally {
|
||||
scheduling.setRunning(taskCopy, false);
|
||||
scheduling.setFinished(task);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Error running " + taskCopy, throwable);
|
||||
} else {
|
||||
log.info("Detected concurrent task execution: {}, skip it", taskCopy);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Error running {}", taskCopy, throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -148,10 +147,10 @@ public class RunnerBean implements Runner {
|
||||
}
|
||||
|
||||
protected ScheduledExecution registerExecutionStart(ScheduledTask task, long now) {
|
||||
if (!BooleanUtils.isTrue(task.getLogStart()) && !BooleanUtils.isTrue(task.getSingleton()))
|
||||
if (!BooleanUtils.isTrue(task.getLogStart()) && !BooleanUtils.isTrue(task.getSingleton()) && task.getSchedulingType() != SchedulingType.FIXED_DELAY)
|
||||
return null;
|
||||
|
||||
log.trace(task + ": registering execution start");
|
||||
log.trace("{}: registering execution start", task);
|
||||
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
@ -172,10 +171,11 @@ public class RunnerBean implements Runner {
|
||||
}
|
||||
|
||||
protected void registerExecutionFinish(ScheduledTask task, ScheduledExecution execution, Object result) {
|
||||
if ((!BooleanUtils.isTrue(task.getLogFinish()) && !BooleanUtils.isTrue(task.getSingleton())) || execution == null)
|
||||
if ((!BooleanUtils.isTrue(task.getLogFinish()) && !BooleanUtils.isTrue(task.getSingleton()) && task.getSchedulingType() != SchedulingType.FIXED_DELAY)
|
||||
|| execution == null)
|
||||
return;
|
||||
|
||||
log.trace(task + ": registering execution finish");
|
||||
log.trace("{}: registering execution finish", task);
|
||||
Transaction tx = persistence.createTransaction();
|
||||
try {
|
||||
EntityManager em = persistence.getEntityManager();
|
||||
@ -193,7 +193,7 @@ public class RunnerBean implements Runner {
|
||||
protected Object executeTask(ScheduledTask task) {
|
||||
switch (task.getDefinedBy()) {
|
||||
case BEAN: {
|
||||
log.trace(task + ": invoking bean");
|
||||
log.trace("{}: invoking bean", task);
|
||||
Object bean = AppBeans.get(task.getBeanName());
|
||||
try {
|
||||
List<MethodParameterInfo> methodParams = task.getMethodParameters();
|
||||
|
@ -33,21 +33,23 @@ import com.haulmont.cuba.security.sys.UserSessionManager;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.perf4j.StopWatch;
|
||||
import org.perf4j.log4j.Log4JStopWatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.support.CronSequenceGenerator;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Class that manages {@link ScheduledTask}s in distributed environment.
|
||||
*
|
||||
*/
|
||||
@Component(SchedulingAPI.NAME)
|
||||
public class Scheduling implements SchedulingAPI {
|
||||
@ -81,7 +83,9 @@ public class Scheduling implements SchedulingAPI {
|
||||
|
||||
protected ConcurrentMap<ScheduledTask, Long> runningTasks = new ConcurrentHashMap<>();
|
||||
|
||||
protected Map<ScheduledTask, Long> lastStartCache = new HashMap<>();
|
||||
protected Map<ScheduledTask, Long> lastStartCache = new ConcurrentHashMap<>();
|
||||
|
||||
protected Map<ScheduledTask, Long> lastFinishCache = new ConcurrentHashMap<>();
|
||||
|
||||
protected volatile long schedulingStartTime;
|
||||
|
||||
@ -136,6 +140,11 @@ public class Scheduling implements SchedulingAPI {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFinished(ScheduledTask task) {
|
||||
lastFinishCache.put(task, timeSource.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return configuration.getConfig(ServerConfig.class).getSchedulingActive();
|
||||
@ -168,7 +177,7 @@ public class Scheduling implements SchedulingAPI {
|
||||
|
||||
protected void processTask(ScheduledTask task) {
|
||||
if (isRunning(task)) {
|
||||
log.trace(task + " is running");
|
||||
log.trace("{} is running", task);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -189,12 +198,18 @@ public class Scheduling implements SchedulingAPI {
|
||||
|
||||
if (BooleanUtils.isTrue(task.getSingleton())) {
|
||||
if (task.getStartDate() != null || SchedulingType.CRON == task.getSchedulingType()) {
|
||||
long currentStart = calculateCurrentStart(task, task.getLastStart(), now, period, frame);
|
||||
|
||||
if (needToStartNow(now, frame, task.getLastStart(), currentStart)) {
|
||||
long currentStart;
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
currentStart = calculateNextDelayDate(task, task.getLastStart(), coordinator.getLastFinished(task), now, frame, period);
|
||||
} else if (SchedulingType.CRON == task.getSchedulingType()) {
|
||||
currentStart = calculateNextCronDate(task, task.getLastStart(), now, frame);
|
||||
} else {
|
||||
currentStart = calculateNextPeriodDate(task, task.getLastStart(), now, frame, period);
|
||||
}
|
||||
if (needToStartInTimeFrame(now, frame, task.getLastStart(), currentStart)) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace(task + "\n not in time frame to start");
|
||||
log.trace("{}\n not in time frame to start", task);
|
||||
}
|
||||
} else {
|
||||
Integer lastServerPriority = task.getLastStartServer() == null ?
|
||||
@ -208,40 +223,56 @@ public class Scheduling implements SchedulingAPI {
|
||||
boolean giveChanceToPreviousHost = lastServerWasNotMe(task, me)
|
||||
&& (lastServerPriority != null && serverPriority.compareTo(lastServerPriority) > 0);
|
||||
|
||||
if (log.isTraceEnabled())
|
||||
log.trace(task + "\n now=" + now + " lastStart=" + task.getLastStart()
|
||||
+ " lastServer=" + task.getLastStartServer() + " shouldSwitch=" + shouldSwitch
|
||||
+ " giveChanceToPreviousHost=" + giveChanceToPreviousHost);
|
||||
log.trace("{}\n now={} lastStart={} lastServer={} shouldSwitch={} giveChanceToPreviousHost={}",
|
||||
task, now, task.getLastStart(), task.getLastStartServer(), shouldSwitch, giveChanceToPreviousHost);
|
||||
|
||||
if (task.getLastStart() == 0
|
||||
|| shouldSwitch
|
||||
|| (task.getLastStart() + (giveChanceToPreviousHost ? period + period / 2 : period) <= now)) {
|
||||
if (task.getLastStart() == 0 || shouldSwitch) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace(task + "\n time has not come and we shouldn't switch");
|
||||
long delay = giveChanceToPreviousHost ? period + period / 2 : period;
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
long lastFinish = coordinator.getLastFinished(task);
|
||||
if ((task.getLastStart() < lastFinish || !lastFinishCache.containsKey(task)) && lastFinish + delay < now) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace("{}\n time has not come and we shouldn't switch", task);
|
||||
}
|
||||
} else if (task.getLastStart() + delay <= now) {
|
||||
runSingletonTask(task, now, me);
|
||||
} else {
|
||||
log.trace("{}\n time has not come and we shouldn't switch", task);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Long lastStart = lastStartCache.get(task);
|
||||
if (lastStart == null) {
|
||||
lastStart = 0L;
|
||||
}
|
||||
Long lastStart = lastStartCache.getOrDefault(task, 0L);
|
||||
Long lastFinish = lastFinishCache.getOrDefault(task, 0L);
|
||||
if (task.getStartDate() != null || SchedulingType.CRON == task.getSchedulingType()) {
|
||||
long currentStart = calculateCurrentStart(task, lastStart, now, period, frame);
|
||||
|
||||
if (needToStartNow(now, frame, lastStart, currentStart)) {
|
||||
long currentStart;
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
currentStart = calculateNextDelayDate(task, lastStart, lastFinish, now, frame, period);
|
||||
} else if (SchedulingType.CRON == task.getSchedulingType()) {
|
||||
currentStart = calculateNextCronDate(task, lastStart, now, frame);
|
||||
} else {
|
||||
currentStart = calculateNextPeriodDate(task, lastStart, now, frame, period);
|
||||
}
|
||||
if (needToStartInTimeFrame(now, frame, lastStart, currentStart)) {
|
||||
runTask(task, now);
|
||||
} else {
|
||||
log.trace(task + "\n not in time frame to start");
|
||||
log.trace("{}\n not in time frame to start", task);
|
||||
}
|
||||
} else {
|
||||
if (log.isTraceEnabled())
|
||||
log.trace(task + "\n now=" + now + " lastStart= " + lastStart);
|
||||
|
||||
if (now >= lastStart + period) {
|
||||
log.trace("{}\n now={} lastStart={} lastFinish={}", task, now, lastStart, lastFinish);
|
||||
if (SchedulingType.FIXED_DELAY == task.getSchedulingType()) {
|
||||
if ((lastStart == 0 || lastStart < lastFinish) && now >= lastFinish + period) {
|
||||
runTask(task, now);
|
||||
} else {
|
||||
log.trace("{}\n time has not come", task);
|
||||
}
|
||||
} else if (now >= lastStart + period) {
|
||||
runTask(task, now);
|
||||
} else {
|
||||
log.trace(task + "\n time has not come");
|
||||
log.trace("{}\n time has not come", task);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,42 +281,48 @@ public class Scheduling implements SchedulingAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needToStartNow(long now, long frame, long lastStart, long currentStart) {
|
||||
protected boolean needToStartInTimeFrame(long now, long frame, long lastStart, long currentStart) {
|
||||
return currentStart <= now && now < currentStart + frame && lastStart < currentStart;
|
||||
}
|
||||
|
||||
protected long calculateCurrentStart(ScheduledTask task, long lastStart, long now, long period, long frame) {
|
||||
String cron = task.getCron();
|
||||
if (SchedulingType.CRON == task.getSchedulingType()) {
|
||||
StopWatch sw = new Log4JStopWatch("Cron next date calculations");
|
||||
CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(cron, getCurrentTimeZone());
|
||||
//if last start = 0 (task never has run) or to far in the past, we use (NOW - FRAME) timestamp for pivot time
|
||||
//this approach should work fine cause cron works with absolute time
|
||||
long pivotPreviousTime = Math.max(lastStart, now - frame);
|
||||
protected long calculateNextCronDate(ScheduledTask task, long date, long currentDate, long frame) {
|
||||
StopWatch sw = new Log4JStopWatch("Cron next date calculations");
|
||||
CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(task.getCron(), getCurrentTimeZone());
|
||||
//if last start = 0 (task never has run) or to far in the past, we use (NOW - FRAME) timestamp for pivot time
|
||||
//this approach should work fine cause cron works with absolute time
|
||||
long pivotPreviousTime = Math.max(date, currentDate - frame);
|
||||
|
||||
Date currentStart = null;
|
||||
Date nextDate = cronSequenceGenerator.next(new Date(pivotPreviousTime));
|
||||
while (nextDate.getTime() < now) {//if next date is in past try to find next date nearest to now
|
||||
currentStart = nextDate;
|
||||
nextDate = cronSequenceGenerator.next(nextDate);
|
||||
}
|
||||
|
||||
if (currentStart == null) {
|
||||
currentStart = nextDate;
|
||||
}
|
||||
|
||||
log.trace(task + "\n now=" + now + " frame=" + frame
|
||||
+ " currentStart=" + currentStart + " lastStart=" + lastStart + " cron=" + cron);
|
||||
sw.stop();
|
||||
return currentStart.getTime();
|
||||
} else {
|
||||
long repetitions = (now - task.getStartDate().getTime()) / period;
|
||||
long currentStart = task.getStartDate().getTime() + repetitions * period;
|
||||
|
||||
log.trace(task + "\n now=" + now + " frame=" + frame + " repetitions=" + repetitions +
|
||||
" currentStart=" + currentStart + " lastStart=" + lastStart);
|
||||
return currentStart;
|
||||
Date currentStart = null;
|
||||
Date nextDate = cronSequenceGenerator.next(new Date(pivotPreviousTime));
|
||||
while (nextDate.getTime() < currentDate) {//if next date is in past try to find next date nearest to now
|
||||
currentStart = nextDate;
|
||||
nextDate = cronSequenceGenerator.next(nextDate);
|
||||
}
|
||||
|
||||
if (currentStart == null) {
|
||||
currentStart = nextDate;
|
||||
}
|
||||
log.trace("{}\n now={} frame={} currentStart={} lastStart={} cron={}",
|
||||
task, currentDate, frame, currentStart, task.getCron());
|
||||
sw.stop();
|
||||
return currentStart.getTime();
|
||||
}
|
||||
|
||||
protected long calculateNextPeriodDate(ScheduledTask task, long date, long currentDate, long frame, long period) {
|
||||
long repetitions = (currentDate - task.getStartDate().getTime()) / period;
|
||||
long currentStart = task.getStartDate().getTime() + repetitions * period;
|
||||
log.trace("{}\n now={} frame={} repetitions={} currentStart={} lastStart={}",
|
||||
task, currentDate, frame, repetitions, currentStart, date);
|
||||
return currentStart;
|
||||
}
|
||||
|
||||
protected long calculateNextDelayDate(ScheduledTask task, long lastStart, long lastFinish, long currentDate, long frame, long period) {
|
||||
long fromDate = lastFinish != 0 ? lastFinish : task.getStartDate().getTime();
|
||||
long repetitions = (currentDate - fromDate) / period;
|
||||
long currentStart = fromDate + repetitions * period;
|
||||
log.trace("{}\n now={} frame={} repetitions={} currentStart={} lastStart={} lastFinish={}",
|
||||
task, currentDate, frame, repetitions, currentStart, lastStart, lastFinish);
|
||||
return currentStart;
|
||||
}
|
||||
|
||||
protected TimeZone getCurrentTimeZone() {
|
||||
|
@ -55,7 +55,7 @@ public interface SchedulingAPI {
|
||||
void processScheduledTasks(boolean onlyIfActive);
|
||||
|
||||
/**
|
||||
* Mark the sheduled task as running/not running in the internal list. This method should not be used in the
|
||||
* Mark the scheduled task as running/not running in the internal list. This method should not be used in the
|
||||
* application code.
|
||||
* @param task task instance
|
||||
* @param running true to mark as running, false to mark as not running
|
||||
@ -63,6 +63,13 @@ public interface SchedulingAPI {
|
||||
*/
|
||||
boolean setRunning(ScheduledTask task, boolean running);
|
||||
|
||||
/**
|
||||
* Mark the scheduled task as finished in the internal list. This method should not be used in the
|
||||
* application code.
|
||||
* @param task task instance
|
||||
*/
|
||||
void setFinished(ScheduledTask task);
|
||||
|
||||
/**
|
||||
* @return a list of active task instances in detached state
|
||||
*/
|
||||
|
@ -41,7 +41,6 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.inject.Inject;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
@ -131,16 +130,23 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyConstraints(Collection<Entity> entities) {
|
||||
List<Entity> supportedEntities = entities.stream().filter(e -> e instanceof HasUuid).collect(Collectors.toList());
|
||||
internalApplyConstraints(supportedEntities, new HashSet<>());
|
||||
public boolean filterByConstraints(Entity entity) {
|
||||
return entity instanceof HasUuid && !isPermittedInMemory(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyConstraints(Entity entity) {
|
||||
if (!(entity instanceof HasUuid))
|
||||
return false;
|
||||
return internalApplyConstraints(entity, new HashSet<>());
|
||||
public void applyConstraints(Collection<Entity> entities) {
|
||||
Set<UUID> handled = new LinkedHashSet<>();
|
||||
entities.stream().filter(entity -> entity instanceof HasUuid).forEach(entity -> {
|
||||
internalApplyConstraints(entity, handled, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyConstraints(Entity entity) {
|
||||
if (entity instanceof HasUuid) {
|
||||
internalApplyConstraints(entity, new HashSet<>(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -209,13 +215,13 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
|
||||
}
|
||||
} catch (JpqlSyntaxException e) {
|
||||
log.error("Syntax errors found in constraint's JPQL expressions. Entity [{}]. Constraint ID [{}].",
|
||||
entityName, constraint.getId(), e);
|
||||
entityName, constraint.getId(), e);
|
||||
|
||||
throw new RowLevelSecurityException(
|
||||
"Syntax errors found in constraint's JPQL expressions. Please see the logs.", entityName);
|
||||
} catch (Exception e) {
|
||||
log.error("An error occurred when applying security constraint. Entity [{}]. Constraint ID [{}].",
|
||||
entityName, constraint.getId(), e);
|
||||
entityName, constraint.getId(), e);
|
||||
|
||||
throw new RowLevelSecurityException(
|
||||
"An error occurred when applying security constraint. Please see the logs.", entityName);
|
||||
@ -226,7 +232,7 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
|
||||
Set<UUID> filtered = new LinkedHashSet<>();
|
||||
for (Iterator<Entity> iterator = entities.iterator(); iterator.hasNext(); ) {
|
||||
Entity next = iterator.next();
|
||||
if (internalApplyConstraints(next, handled)) {
|
||||
if (internalApplyConstraints(next, handled, true)) {
|
||||
filtered.add(((HasUuid) next).getUuid());
|
||||
//we ignore situations when the collection is immutable
|
||||
iterator.remove();
|
||||
@ -237,10 +243,10 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean internalApplyConstraints(Entity entity, Set<UUID> handled) {
|
||||
protected boolean internalApplyConstraints(Entity entity, Set<UUID> handled, boolean checkPermitted) {
|
||||
MetaClass metaClass = entity.getMetaClass();
|
||||
|
||||
if (!isPermittedInMemory(entity)) {
|
||||
if (!isPermittedInMemory(entity) && checkPermitted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -260,7 +266,7 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
|
||||
}
|
||||
} else if (value instanceof Entity && value instanceof HasUuid) {
|
||||
Entity valueEntity = (Entity) value;
|
||||
if (internalApplyConstraints(valueEntity, handled)) {
|
||||
if (internalApplyConstraints(valueEntity, handled, true)) {
|
||||
//we ignore the situation when the field is read-only
|
||||
entity.setValue(property.getName(), null);
|
||||
if (entity instanceof BaseGenericIdEntity) {
|
||||
|
@ -60,42 +60,42 @@ public class SchedulingTest {
|
||||
scheduledTask.setCron("*/5 * * * * *");
|
||||
|
||||
//scheduler has failed couple of runs and now we should run it
|
||||
long currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 0, 10000l);
|
||||
long currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-13 15:29:55"), new Date(currentStart));
|
||||
|
||||
//last run was year ago, so now-frame should be considered
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2012-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 0, 10000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2012-11-13 15:29:00").getTime(), date("2013-11-13 15:30:00").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-13 15:29:55"), new Date(currentStart));
|
||||
|
||||
//last run was very close to now, last start date should be considered
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-13 15:29:59").getTime(), date("2013-11-13 15:30:01").getTime(), 0, 10000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-13 15:29:59").getTime(), date("2013-11-13 15:30:01").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-13 15:30:00"), new Date(currentStart));
|
||||
|
||||
scheduledTask.setCron("0 0 0 * * FRI");
|
||||
|
||||
//task should run in next friday
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-13 15:30:00").getTime(), 0, 10000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-13 15:30:00").getTime(), 10000l);
|
||||
assertEquals(date("2013-11-15 00:00:00"), new Date(currentStart));
|
||||
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-08 01:01:02").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-08 01:01:01").getTime(), date("2013-11-08 01:01:02").getTime(), 600000l);
|
||||
assertEquals(date("2013-11-15 00:00:00"), new Date(currentStart));
|
||||
|
||||
//task is late but matches frame
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:01:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:01:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-11-8 00:00:00"), new Date(currentStart));
|
||||
|
||||
//task is late and does not match frame
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:11:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-11-07 23:59:59").getTime(), date("2013-11-08 00:11:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-11-15 00:00:00"), new Date(currentStart));
|
||||
|
||||
scheduledTask.setCron("0 59 1 * * *");
|
||||
|
||||
//time shift forward
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-10-26 1:59:59").getTime(), date("2013-10-27 00:00:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-10-26 1:59:59").getTime(), date("2013-10-27 00:00:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-10-27 01:59:00"), new Date(currentStart));
|
||||
|
||||
//time shift backward
|
||||
currentStart = scheduling.calculateCurrentStart(scheduledTask, date("2013-03-30 1:59:00").getTime(), date("2013-03-31 00:00:00").getTime(), 0, 600000l);
|
||||
currentStart = scheduling.calculateNextCronDate(scheduledTask, date("2013-03-30 1:59:00").getTime(), date("2013-03-31 00:00:00").getTime(), 600000l);
|
||||
assertEquals(date("2013-03-31 01:59:00"), new Date(currentStart));
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,6 @@ import java.awt.event.WindowEvent;
|
||||
import java.util.*;
|
||||
|
||||
public class DesktopTabSheet extends DesktopAbstractComponent<JTabbedPane> implements TabSheet, DesktopContainer, AutoExpanding {
|
||||
|
||||
protected Map<Component, String> components = new HashMap<>();
|
||||
|
||||
protected List<TabImpl> tabs = new ArrayList<>();
|
||||
@ -61,8 +60,16 @@ public class DesktopTabSheet extends DesktopAbstractComponent<JTabbedPane> imple
|
||||
|
||||
protected Set<TabChangeListener> listeners = new HashSet<>();
|
||||
|
||||
// CAUTION do not add ChangeListeners directly to impl
|
||||
protected List<ChangeListener> implTabSheetChangeListeners = new ArrayList<>();
|
||||
|
||||
public DesktopTabSheet() {
|
||||
impl = new JTabbedPaneExt();
|
||||
impl.addChangeListener(e -> {
|
||||
for (ChangeListener listener : new ArrayList<>(implTabSheetChangeListeners)) {
|
||||
listener.stateChanged(e);
|
||||
}
|
||||
});
|
||||
|
||||
setWidth("100%");
|
||||
}
|
||||
@ -239,26 +246,16 @@ public class DesktopTabSheet extends DesktopAbstractComponent<JTabbedPane> imple
|
||||
lazyTabs.add(new LazyTabInfo(tab, tabContent, descriptor, loader));
|
||||
|
||||
if (!initLazyTabListenerAdded) {
|
||||
impl.addChangeListener(
|
||||
new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
initLazyTab((JComponent) impl.getSelectedComponent());
|
||||
}
|
||||
}
|
||||
);
|
||||
implTabSheetChangeListeners.add(new LazyTabChangeListener());
|
||||
initLazyTabListenerAdded = true;
|
||||
}
|
||||
|
||||
context = loader.getContext();
|
||||
|
||||
if (!postInitTaskAdded) {
|
||||
context.addPostInitTask(new ComponentLoader.PostInitTask() {
|
||||
@Override
|
||||
public void execute(ComponentLoader.Context context, Frame window) {
|
||||
initComponentTabChangeListener();
|
||||
}
|
||||
});
|
||||
context.addPostInitTask((context1, window) ->
|
||||
initComponentTabChangeListener()
|
||||
);
|
||||
postInitTaskAdded = true;
|
||||
}
|
||||
return tab;
|
||||
@ -374,34 +371,45 @@ public class DesktopTabSheet extends DesktopAbstractComponent<JTabbedPane> imple
|
||||
// init component SelectedTabChangeListener only when needed, making sure it is
|
||||
// after all lazy tabs listeners
|
||||
if (!componentTabChangeListenerInitialized) {
|
||||
impl.addChangeListener(e -> {
|
||||
if (context != null) {
|
||||
context.executeInjectTasks();
|
||||
context.executePostWrapTasks();
|
||||
}
|
||||
// Init lazy tab if needed
|
||||
initLazyTab((JComponent) impl.getSelectedComponent());
|
||||
|
||||
// Fire GUI listener
|
||||
fireTabChanged();
|
||||
|
||||
// Execute outstanding post init tasks after GUI listener.
|
||||
// We suppose that context.executePostInitTasks() executes a task once and then remove it from task list.
|
||||
if (context != null) {
|
||||
context.executePostInitTasks();
|
||||
}
|
||||
|
||||
Window window = ComponentsHelper.getWindow(DesktopTabSheet.this);
|
||||
if (window != null) {
|
||||
((DsContextImplementation) window.getDsContext()).resumeSuspended();
|
||||
} else {
|
||||
log.warn("Please specify Frame for TabSheet");
|
||||
}
|
||||
});
|
||||
implTabSheetChangeListeners.add(new ComponentTabChangeListener());
|
||||
componentTabChangeListenerInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected class ComponentTabChangeListener implements ChangeListener {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (context != null) {
|
||||
context.executeInjectTasks();
|
||||
context.executePostWrapTasks();
|
||||
context.executeInitTasks();
|
||||
}
|
||||
|
||||
// Fire GUI listener
|
||||
fireTabChanged();
|
||||
|
||||
// Execute outstanding post init tasks after GUI listener.
|
||||
// We suppose that context.executePostInitTasks() executes a task once and then remove it from task list.
|
||||
if (context != null) {
|
||||
context.executePostInitTasks();
|
||||
}
|
||||
|
||||
Window window = ComponentsHelper.getWindow(DesktopTabSheet.this);
|
||||
if (window != null) {
|
||||
((DsContextImplementation) window.getDsContext()).resumeSuspended();
|
||||
} else {
|
||||
log.warn("Please specify Frame for TabSheet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class LazyTabChangeListener implements ChangeListener {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
initLazyTab((JComponent) impl.getSelectedComponent());
|
||||
}
|
||||
}
|
||||
|
||||
protected void initLazyTab(JComponent tab) {
|
||||
LazyTabInfo lti = null;
|
||||
for (LazyTabInfo lazyTabInfo : lazyTabs) {
|
||||
|
@ -100,6 +100,12 @@ public class CategoryAttribute extends StandardEntity {
|
||||
@Column(name = "DEFAULT_DATE_IS_CURRENT")
|
||||
private Boolean defaultDateIsCurrent;
|
||||
|
||||
@Column(name = "WIDTH", length = 20)
|
||||
private String width;
|
||||
|
||||
@Column(name = "ROWS_COUNT")
|
||||
private Integer rowsCount;
|
||||
|
||||
public void setCategory(Category entityType) {
|
||||
this.category = entityType;
|
||||
}
|
||||
@ -268,6 +274,22 @@ public class CategoryAttribute extends StandardEntity {
|
||||
this.categoryEntityType = categoryEntityType;
|
||||
}
|
||||
|
||||
public String getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public Integer getRowsCount() {
|
||||
return rowsCount;
|
||||
}
|
||||
|
||||
public void setRowsCount(Integer rowsCount) {
|
||||
this.rowsCount = rowsCount;
|
||||
}
|
||||
|
||||
public Set<String> targetScreensSet() {
|
||||
if (StringUtils.isNotBlank(targetScreens)) {
|
||||
return new HashSet<>(Arrays.asList(targetScreens.split(",")));
|
||||
|
@ -20,7 +20,8 @@ import com.haulmont.chile.core.datatypes.impl.EnumClass;
|
||||
|
||||
public enum SchedulingType implements EnumClass<String> {
|
||||
CRON("C"),
|
||||
PERIOD("P");
|
||||
PERIOD("P"),
|
||||
FIXED_DELAY("D");
|
||||
|
||||
private final String id;
|
||||
|
||||
|
@ -99,6 +99,8 @@ CategoryAttribute.orderNo = Order No
|
||||
CategoryAttribute.lookup = Select with lookup field
|
||||
CategoryAttribute.targetScreens = Target screens
|
||||
CategoryAttribute.defaultDateIsCurrent = Default date is current
|
||||
CategoryAttribute.width=Width
|
||||
CategoryAttribute.rowsCount=Rows count
|
||||
|
||||
ScheduledTask=Scheduled Task
|
||||
ScheduledTask.beanName=Bean Name
|
||||
@ -132,6 +134,7 @@ ScheduledTask.methodParamsXml = XML
|
||||
|
||||
SchedulingType.CRON=Cron
|
||||
SchedulingType.PERIOD=Period
|
||||
SchedulingType.FIXED_DELAY=Fixed Delay
|
||||
|
||||
ScheduledExecution = Scheduled Execution
|
||||
ScheduledExecution.task = Task
|
||||
|
@ -102,6 +102,8 @@ CategoryAttribute.orderNo=Порядковый номер
|
||||
CategoryAttribute.lookup=Выбирать в выпадающем списке
|
||||
CategoryAttribute.targetScreens = Экраны
|
||||
CategoryAttribute.defaultDateIsCurrent=Дата по умолчанию равна текущей
|
||||
CategoryAttribute.width=Ширина
|
||||
CategoryAttribute.rowsCount=Количество строк
|
||||
|
||||
ScheduledTask=Назначенное задание
|
||||
ScheduledTask.beanName=Bean Name
|
||||
@ -135,6 +137,7 @@ ScheduledTask.methodParamsXml = XML
|
||||
|
||||
SchedulingType.CRON=Cron
|
||||
SchedulingType.PERIOD=Period
|
||||
SchedulingType.FIXED_DELAY=Fixed Delay
|
||||
|
||||
ScheduledExecution = Выполнение задания
|
||||
ScheduledExecution.task = Task
|
||||
|
@ -837,6 +837,10 @@ public abstract class WindowManager {
|
||||
context.executeInjectTasks();
|
||||
context.setFrame(component);
|
||||
context.executePostWrapTasks();
|
||||
|
||||
// init of frame
|
||||
context.executeInitTasks();
|
||||
|
||||
context.executePostInitTasks();
|
||||
|
||||
loadDescriptorWatch.stop();
|
||||
@ -1020,6 +1024,8 @@ public abstract class WindowManager {
|
||||
context.executePostWrapTasks();
|
||||
|
||||
init(wrappingWindow, params);
|
||||
|
||||
context.executeInitTasks();
|
||||
}
|
||||
|
||||
protected void init(Window window, Map<String, Object> params) {
|
||||
|
@ -52,16 +52,23 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
|
||||
static {
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.BOOLEAN, "defaultBoolean");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.STRING, "defaultString");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.STRING, "width");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.STRING, "rowsCount");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.DOUBLE, "defaultDouble");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.DOUBLE, "width");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.INTEGER, "defaultInt");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.INTEGER, "width");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.DATE, "defaultDate");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.DATE, "defaultDateIsCurrent");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.DATE, "width");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENUMERATION, "enumeration");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENUMERATION, "defaultString");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENUMERATION, "width");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "entityClass");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "screen");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "lookup");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "defaultEntityId");
|
||||
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "width");
|
||||
}
|
||||
|
||||
protected CategoryAttribute attribute;
|
||||
|
@ -44,6 +44,14 @@
|
||||
<field id="entityClass" custom="true" required="true" width="100%"/>
|
||||
<field id="screen" custom="true" width="100%"/>
|
||||
<field id="lookup"/>
|
||||
<field id="width" width="100%">
|
||||
<validator class="com.haulmont.cuba.gui.components.validators.PatternValidator"
|
||||
pattern="^(\d*(\.\d+)?)(%|px)?$" message="msg://widthValidationMsg"/>
|
||||
</field>
|
||||
<field id="rowsCount" width="100%">
|
||||
<validator class="com.haulmont.cuba.gui.components.validators.IntegerValidator"
|
||||
onlyPositive="true" message="msg://rowsCountValidationMsg"/>
|
||||
</field>
|
||||
<field id="defaultEntityId" custom="true" width="100%"/>
|
||||
|
||||
<field id="enumeration" required="true" width="100%"/>
|
||||
|
@ -63,6 +63,8 @@ entityScreenRequired=Entity screen required
|
||||
enumRequired=Enumeration required
|
||||
uniqueName=Attribute with same name already exists
|
||||
uniqueCode=Attribute with same code already exists
|
||||
widthValidationMsg=Width is incorrect
|
||||
rowsCountValidationMsg=Rows count should be a positive number
|
||||
|
||||
msgTrue=True
|
||||
msgFalse=False
|
||||
|
@ -58,6 +58,8 @@ nameRequired=Заполните поле "Название"
|
||||
dataTypeRequired=Заполните поле "Тип атрибута"
|
||||
entityTypeRequired=Заполните поле "Тип сущности"
|
||||
entityScreenRequired=Заполните поле "Экран выбора сущности"
|
||||
widthValidationMsg=Поле "Ширина" указано в некорректном формате
|
||||
rowsCountValidationMsg=Поле "Количество строк" должно быть положительным числом
|
||||
enumRequired=Заполните поле "Перечисление"
|
||||
|
||||
msgTrue=Да
|
||||
|
@ -35,6 +35,7 @@ import com.haulmont.cuba.gui.ComponentsHelper;
|
||||
import com.haulmont.cuba.gui.data.CollectionDatasource;
|
||||
import com.haulmont.cuba.gui.data.Datasource;
|
||||
import com.haulmont.cuba.gui.data.DsBuilder;
|
||||
import com.haulmont.cuba.gui.data.RuntimePropsDatasource;
|
||||
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
|
||||
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@ -186,6 +187,20 @@ public abstract class AbstractFieldFactory implements FieldFactory {
|
||||
textField = textArea;
|
||||
}
|
||||
}
|
||||
if (DynamicAttributesUtils.isDynamicAttribute(property)) {
|
||||
MetaClass metaClass = datasource instanceof RuntimePropsDatasource ?
|
||||
((RuntimePropsDatasource) datasource).resolveCategorizedEntityClass() : datasource.getMetaClass();
|
||||
MetaPropertyPath mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
|
||||
if (mpp != null) {
|
||||
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(mpp.getMetaProperty());
|
||||
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.STRING
|
||||
&& categoryAttribute.getRowsCount() != null && categoryAttribute.getRowsCount() > 1) {
|
||||
TextArea textArea = componentsFactory.createComponent(TextArea.class);
|
||||
textArea.setRows(categoryAttribute.getRowsCount());
|
||||
textField = textArea;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textField == null) {
|
||||
textField = componentsFactory.createComponent(TextField.class);
|
||||
|
@ -179,8 +179,17 @@ public class RuntimePropertiesFrame extends AbstractWindow {
|
||||
for (DynamicAttributesMetaProperty property : metaProperties) {
|
||||
FieldGroup.FieldConfig field = new FieldGroup.FieldConfig(property.getName());
|
||||
CategoryAttribute attribute = property.getAttribute();
|
||||
field.setCaption(attribute != null ? attribute.getName() : property.getName());
|
||||
field.setWidth(fieldWidth);
|
||||
if (attribute != null) {
|
||||
field.setCaption(attribute.getName());
|
||||
if (StringUtils.isNotBlank(attribute.getWidth())) {
|
||||
field.setWidth(attribute.getWidth());
|
||||
} else {
|
||||
field.setWidth(fieldWidth);
|
||||
}
|
||||
} else {
|
||||
field.setCaption(property.getName());
|
||||
field.setWidth(fieldWidth);
|
||||
}
|
||||
fields.add(field);
|
||||
Range range = property.getRange();
|
||||
if (!range.isDatatype()) {
|
||||
|
@ -971,6 +971,7 @@
|
||||
<xs:attributeGroup ref="hasCaption"/>
|
||||
<xs:attributeGroup ref="hasVisibility"/>
|
||||
<xs:attributeGroup ref="hasEnableProp"/>
|
||||
<xs:attributeGroup ref="hasStyle"/>
|
||||
|
||||
<xs:attributeGroup ref="isUploadComponent"/>
|
||||
</xs:complexType>
|
||||
@ -984,6 +985,7 @@
|
||||
<xs:attributeGroup ref="hasVisibility"/>
|
||||
<xs:attributeGroup ref="hasEnableProp"/>
|
||||
<xs:attributeGroup ref="hasIcon"/>
|
||||
<xs:attributeGroup ref="hasStyle"/>
|
||||
|
||||
<xs:attributeGroup ref="isUploadComponent"/>
|
||||
</xs:complexType>
|
||||
|
@ -44,6 +44,9 @@ public interface ComponentLoader<T extends Component> {
|
||||
void addInjectTask(InjectTask task);
|
||||
void executeInjectTasks();
|
||||
|
||||
void addInitTask(InitTask task);
|
||||
void executeInitTasks();
|
||||
|
||||
Frame getFrame();
|
||||
void setFrame(Frame frame);
|
||||
|
||||
@ -85,6 +88,19 @@ public interface ComponentLoader<T extends Component> {
|
||||
void execute(Context context, Frame window);
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal use only.
|
||||
*/
|
||||
interface InitTask {
|
||||
/**
|
||||
* This method will be invoked after window components loading before window initialization.
|
||||
*
|
||||
* @param context loader context
|
||||
* @param window top-most window
|
||||
*/
|
||||
void execute(Context context, Frame window);
|
||||
}
|
||||
|
||||
/**
|
||||
* PostInitTasks are used to perform deferred initialization of visual components that requires window controller.
|
||||
*/
|
||||
|
@ -37,6 +37,7 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
protected List<ComponentLoader.PostInitTask> postInitTasks = new ArrayList<>();
|
||||
protected List<ComponentLoader.InjectTask> injectTasks = new ArrayList<>();
|
||||
protected List<ComponentLoader.PostWrapTask> postWrapTasks = new ArrayList<>();
|
||||
protected List<ComponentLoader.InitTask> initTasks = new ArrayList<>();
|
||||
|
||||
protected Map<String, Object> parameters;
|
||||
|
||||
@ -148,6 +149,18 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitTask(ComponentLoader.InitTask task) {
|
||||
initTasks.add(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeInitTasks() {
|
||||
if (!getInitTasks().isEmpty()) {
|
||||
new InitTaskExecutor(getInitTasks().get(0)).run();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ComponentLoader.InjectTask> getInjectTasks() {
|
||||
return injectTasks;
|
||||
}
|
||||
@ -160,6 +173,10 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
return postWrapTasks;
|
||||
}
|
||||
|
||||
public List<ComponentLoader.InitTask> getInitTasks() {
|
||||
return initTasks;
|
||||
}
|
||||
|
||||
protected void removeTask(ComponentLoader.PostInitTask task, ComponentLoaderContext context) {
|
||||
if (context.getPostInitTasks().remove(task) && context.getParent() != null) {
|
||||
removeTask(task, (ComponentLoaderContext) context.getParent());
|
||||
@ -178,11 +195,17 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskExecutor implements Runnable {
|
||||
protected void removeTask(ComponentLoader.InitTask task, ComponentLoaderContext context) {
|
||||
if (context.getInitTasks().remove(task) && context.getParent() != null) {
|
||||
removeTask(task, (ComponentLoaderContext) context.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
protected class TaskExecutor implements Runnable {
|
||||
|
||||
private final ComponentLoader.PostInitTask task;
|
||||
|
||||
private TaskExecutor(ComponentLoader.PostInitTask task) {
|
||||
public TaskExecutor(ComponentLoader.PostInitTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@ -196,10 +219,10 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
}
|
||||
}
|
||||
|
||||
private class InjectTaskExecutor implements Runnable {
|
||||
protected class InjectTaskExecutor implements Runnable {
|
||||
private final ComponentLoader.InjectTask task;
|
||||
|
||||
private InjectTaskExecutor(ComponentLoader.InjectTask task) {
|
||||
public InjectTaskExecutor(ComponentLoader.InjectTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@ -213,10 +236,10 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
}
|
||||
}
|
||||
|
||||
private class PostWrapTaskExecutor implements Runnable {
|
||||
protected class PostWrapTaskExecutor implements Runnable {
|
||||
private final ComponentLoader.PostWrapTask task;
|
||||
|
||||
private PostWrapTaskExecutor(ComponentLoader.PostWrapTask task) {
|
||||
public PostWrapTaskExecutor(ComponentLoader.PostWrapTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@ -229,4 +252,21 @@ public class ComponentLoaderContext implements ComponentLoader.Context {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class InitTaskExecutor implements Runnable {
|
||||
private final ComponentLoader.InitTask task;
|
||||
|
||||
public InitTaskExecutor(ComponentLoader.InitTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
removeTask(task, ComponentLoaderContext.this);
|
||||
task.execute(ComponentLoaderContext.this, frame);
|
||||
if (!getInitTasks().isEmpty()) {
|
||||
new InitTaskExecutor(getInitTasks().get(0)).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -69,6 +69,7 @@ public class FieldGroupLoader extends AbstractComponentLoader<FieldGroup> {
|
||||
messages.getMainMessagePack(),
|
||||
"validation.required.defaultMsg",
|
||||
attribute.getName()));
|
||||
loadWidth(field, attribute.getWidth());
|
||||
fields.add(field);
|
||||
}
|
||||
dynamicAttributesGuiTools.listenDynamicAttributesChanges(ds);
|
||||
@ -197,7 +198,7 @@ public class FieldGroupLoader extends AbstractComponentLoader<FieldGroup> {
|
||||
|
||||
field.setFormatter(loadFormatter(element));
|
||||
|
||||
loadWidth(field, element);
|
||||
loadWidth(field, element.attributeValue("width"));
|
||||
|
||||
field.setCustom(customField);
|
||||
|
||||
@ -215,8 +216,7 @@ public class FieldGroupLoader extends AbstractComponentLoader<FieldGroup> {
|
||||
return field;
|
||||
}
|
||||
|
||||
protected void loadWidth(FieldGroup.FieldConfig field, Element element) {
|
||||
final String width = element.attributeValue("width");
|
||||
protected void loadWidth(FieldGroup.FieldConfig field, String width) {
|
||||
if ("auto".equalsIgnoreCase(width)) {
|
||||
field.setWidth(Component.AUTO_SIZE);
|
||||
} else if (StringUtils.isNotBlank(width)) {
|
||||
|
@ -62,6 +62,7 @@ public class FrameLoader<T extends Frame> extends ContainerLoader<T> {
|
||||
parentContext.addInjectTask(new FrameInjectPostInitTask(wrappingFrame, params));
|
||||
|
||||
boolean wrapped = StringUtils.isNotBlank(rootFrameElement.attributeValue("class"));
|
||||
parentContext.addInitTask(new FrameLoaderInitTask(wrappingFrame, params, wrapped));
|
||||
parentContext.addPostInitTask(new FrameLoaderPostInitTask(wrappingFrame, params, wrapped));
|
||||
}
|
||||
|
||||
@ -176,6 +177,7 @@ public class FrameLoader<T extends Frame> extends ContainerLoader<T> {
|
||||
initWrapperFrame(resultComponent, element, parentContext.getParams(), parentContext);
|
||||
|
||||
parentContext.getInjectTasks().addAll(innerContext.getInjectTasks());
|
||||
parentContext.getInitTasks().addAll(innerContext.getInitTasks());
|
||||
parentContext.getPostInitTasks().addAll(innerContext.getPostInitTasks());
|
||||
|
||||
setContext(parentContext);
|
||||
@ -229,13 +231,12 @@ public class FrameLoader<T extends Frame> extends ContainerLoader<T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected class FrameLoaderPostInitTask implements PostInitTask {
|
||||
|
||||
protected class FrameLoaderInitTask implements InitTask {
|
||||
protected final Frame frame;
|
||||
protected final Map<String, Object> params;
|
||||
protected final boolean wrapped;
|
||||
|
||||
public FrameLoaderPostInitTask(Frame frame, Map<String, Object> params, boolean wrapped) {
|
||||
public FrameLoaderInitTask(Frame frame, Map<String, Object> params, boolean wrapped) {
|
||||
this.frame = frame;
|
||||
this.params = params;
|
||||
this.wrapped = wrapped;
|
||||
@ -255,6 +256,25 @@ public class FrameLoader<T extends Frame> extends ContainerLoader<T> {
|
||||
|
||||
initStopWatch.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class FrameLoaderPostInitTask implements PostInitTask {
|
||||
protected final Frame frame;
|
||||
protected final Map<String, Object> params;
|
||||
protected final boolean wrapped;
|
||||
|
||||
public FrameLoaderPostInitTask(Frame frame, Map<String, Object> params, boolean wrapped) {
|
||||
this.frame = frame;
|
||||
this.params = params;
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Context context, Frame window) {
|
||||
if (wrapped) {
|
||||
String loggingId = ComponentsHelper.getFullFrameId(this.frame);
|
||||
|
||||
StopWatch uiPermissionsWatch = new Log4JStopWatch(loggingId + "#" +
|
||||
UIPerformanceLogger.LifeCycle.UI_PERMISSIONS,
|
||||
|
@ -37,7 +37,6 @@ public class CubaResizableTextAreaWrapperWidget extends VCustomComponent {
|
||||
|
||||
protected static final int MOUSE_EVENTS = Event.ONMOUSEDOWN | Event.ONMOUSEMOVE | Event.ONMOUSEUP | Event.ONMOUSEOVER;
|
||||
protected static final int MINIMAL_WIDTH = 17;
|
||||
protected static final int MINIMAL_HEIGHT = 17;
|
||||
|
||||
public boolean isResizable() {
|
||||
return resizeElement != null;
|
||||
@ -132,8 +131,10 @@ public class CubaResizableTextAreaWrapperWidget extends VCustomComponent {
|
||||
int absoluteLeft = getAbsoluteLeft();
|
||||
int absoluteTop = getAbsoluteTop();
|
||||
|
||||
ComputedStyle cs = new ComputedStyle(getElement().getFirstChildElement());
|
||||
|
||||
//do not allow mirror-functionality
|
||||
if (mouseY > absoluteTop + MINIMAL_HEIGHT && mouseX > absoluteLeft + MINIMAL_WIDTH) {
|
||||
if (mouseY > absoluteTop + cs.getDoubleProperty("min-height") && mouseX > absoluteLeft + MINIMAL_WIDTH) {
|
||||
int width = mouseX - absoluteLeft + 2;
|
||||
int height = mouseY - absoluteTop + 2;
|
||||
|
||||
|
@ -404,6 +404,7 @@ public class WebAccordion extends WebAbstractComponent<CubaAccordion> implements
|
||||
if (context != null) {
|
||||
context.executeInjectTasks();
|
||||
context.executePostWrapTasks();
|
||||
context.executeInitTasks();
|
||||
}
|
||||
// Fire GUI listener
|
||||
fireTabChanged();
|
||||
|
@ -310,7 +310,9 @@ public class WebTabSheet extends WebAbstractComponent<CubaTabSheet> implements T
|
||||
context = loader.getContext();
|
||||
|
||||
if (!postInitTaskAdded) {
|
||||
context.addPostInitTask((context1, window) -> initComponentTabChangeListener());
|
||||
context.addPostInitTask((context1, window) ->
|
||||
initComponentTabChangeListener()
|
||||
);
|
||||
postInitTaskAdded = true;
|
||||
}
|
||||
|
||||
@ -435,6 +437,7 @@ public class WebTabSheet extends WebAbstractComponent<CubaTabSheet> implements T
|
||||
if (context != null) {
|
||||
context.executeInjectTasks();
|
||||
context.executePostWrapTasks();
|
||||
context.executeInitTasks();
|
||||
}
|
||||
// Fire GUI listener
|
||||
fireTabChanged();
|
||||
|
@ -21,6 +21,10 @@
|
||||
.cuba-resizabletextarea-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.v-textarea {
|
||||
min-height: $v-unit-size;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-resize-corner {
|
||||
|
@ -18,6 +18,10 @@
|
||||
.cuba-resizabletextarea-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.v-textarea {
|
||||
min-height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$primary-stylename}-resize-corner {
|
||||
|
Loading…
Reference in New Issue
Block a user