소스 검색

Merge branch 'master' into kris/return_ctx_from_script_run

tags/2.0.1
jonathan 4 년 전
부모
커밋
881ef86352
6개의 변경된 파일83개의 추가작업 그리고 338개의 파일을 삭제
  1. +13
    -0
      wizard-common/src/main/java/org/cobbzilla/wizard/client/ApiClientBase.java
  2. +0
    -6
      wizard-server-test/pom.xml
  3. +0
    -240
      wizard-server-test/src/main/java/org/cobbzilla/wizardtest/time/SystemTimeShifter.java
  4. +0
    -73
      wizard-server/src/main/java/org/cobbzilla/wizard/cache/redis/ActivationCodeService.java
  5. +41
    -11
      wizard-server/src/main/java/org/cobbzilla/wizard/cache/redis/RedisService.java
  6. +29
    -8
      wizard-server/src/main/java/org/cobbzilla/wizard/dao/AbstractCRUDDAO.java

+ 13
- 0
wizard-common/src/main/java/org/cobbzilla/wizard/client/ApiClientBase.java 파일 보기

@@ -273,6 +273,16 @@ public class ApiClientBase implements Cloneable, Closeable {
return new RestResponse(response);
}

public RestResponse uploadStream(String path, InputStream in, String name, String method) throws Exception {
final String url = getUrl(path, getBaseUri());
final NameAndValue[] headers = { new NameAndValue(getTokenHeader(), token) };

final HttpRequestBean request = new HttpRequestBean(method, url, in, name, headers);
final HttpResponseBean response = HttpUtil.getStreamResponse(request);

return new RestResponse(response);
}

public <T> T post(String path, T request) throws Exception {
if (request instanceof ModelEntity) {
return (T) post(path, ((ModelEntity) request).getEntity(), ((ModelEntity) request).getEntity().getClass());
@@ -353,6 +363,9 @@ public class ApiClientBase implements Cloneable, Closeable {
} else if (path.startsWith("/") && clientUri.endsWith("/")) {
path = path.substring(1); // caller has supplied a relative path
}
if (!path.startsWith("/") && !clientUri.endsWith("/")) {
clientUri = clientUri + "/"; // client URI does not end with a slash, add one
}
return clientUri + path;
}



+ 0
- 6
wizard-server-test/pom.xml 파일 보기

@@ -29,12 +29,6 @@ This code is available under the Apache License, version 2: http://www.apache.or
<version>${junit.version}</version>
</dependency>

<dependency>
<groupId>com.googlecode.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.1</version>
</dependency>

<dependency>
<groupId>org.cobbzilla</groupId>
<artifactId>restex</artifactId>


+ 0
- 240
wizard-server-test/src/main/java/org/cobbzilla/wizardtest/time/SystemTimeShifter.java 파일 보기

@@ -1,240 +0,0 @@
package org.cobbzilla.wizardtest.time;

import mockit.Mock;
import mockit.MockClass;
import mockit.Mockit;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import static org.cobbzilla.util.daemon.ZillaRuntime.now;

/**
* Found at: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/
*
* Class changes the system time returned by {@link System#currentTimeMillis()} via JMockit weaving.
* <p/>
* Original System class can be restored any time calling {@link #reset()} method. There are a few ways how to specify modified system time:
* <ul>
* <li>setting ms offset via {@link #setOffset(long)}
* <li>changing ms offset (relatively) via {@link #changeOffset(long)}
* <li>setting new date, time or ISO date/time via {@link #setIsoDate(String)}
* </ul>
* <p/>
* Any of these methods can be used through system properties (-D) this way (first property in this order is used, others ignored):
* <ul>
* <li>{@code -Dsystime.offset=1000} - shift by one second to the future (negative number can be used)
* <li>{@code -Dsystime.millis=1000} - set system time one second after start of the era (1970...)
* <li>{@code -Dsystime.iso=2000-01-01T00:00:47} - 47 seconds after beginning of the 2000, alternatively you can set only time (00:00:47, date stays current) or
* only date (2000-01-01, current time) without 'T' in both cases.
* </ul>
* <p/>
* There must be something that causes class load, otherwise nothing happens. In order to allow this without modifying the original program, one may use this
* class as a main class with original main class as the first argument (they will be correctly shifted when served to the original class). If no relevant
* property is specified via -D, nothing happens. In any case (programmatic or main class replacement) this class has to be on a classpath. For application
* server usage this means it has to be in its system libraries, not in EAR/WAR that is not loaded during the AS start yet.
* <p/>
* Example:
*
* <pre>
* java -Dsystime.iso=2000-01-01T00:00:47 SystemTimeShifter my.uber.appserver.Main arg1 second "third long with spaces"
* </pre>
* <b>WARNING:</b> Sun/Oracle HotSpot JVM and its inline optimization may mess up with the mock after it is set up, so if you notice that the time
* returns to normal after number of invocations, you should add {@code -XX:-Inline} option to your java command line. Other JVM specific options
* may be needed for different JVM implementations.
*
* @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a>
*/
public class SystemTimeShifter {
/**
* System property setting ms offset.
*/
public static final String PROPERTY_OFFSET = "systime.offset";

/**
* System property setting "current" millis.
*/
public static final String PROPERTY_MILLIS = "systime.millis";

/**
* System property setting ISO date/time (or date, or time).
*/
public static final String PROPERTY_ISO_DATE = "systime.iso";

private static final long INIT_MILLIS = now();
private static final long INIT_NANOS = System.nanoTime();
private static long offset;

private static boolean mockInstalled;

@Deprecated
protected SystemTimeShifter() {
// prevents calls from subclass
throw new UnsupportedOperationException();
}

static {
String isoDate = System.getProperty(PROPERTY_ISO_DATE);
String millis = System.getProperty(PROPERTY_MILLIS);
String offset = System.getProperty(PROPERTY_OFFSET);
try {
if (isoDate != null) {
setIsoDate(isoDate);
} else if (millis != null) {
setMillis(Integer.parseInt(millis));
} else if (offset != null) {
setOffset(Integer.parseInt(offset));
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}

/**
* Bootstrap main to allow time shifting before actually loading the real main class. Real
* main class must be the first argument, it will be removed from the list when calling the
* real class. Without using any relevant -D property there will be no time shifting.
*
* @param args argument list with original (desired) class as the first argument
* @throws Exception may happen during the reflection call of the other main
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void main(String[] args) throws Exception {
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, args.length - 1);

Class clazz = Class.forName(args[0]);
Method main = clazz.getMethod("main", newArgs.getClass());
main.invoke(null, (Object) newArgs);
}

/**
* Sets the new "system" time to specified ISO time. It is possible to set exact time with the format {@code yyyy-MM-dd'T'HH:mm:ss} (no apostrophes around T
* in the actual string!) or one can set just time
* (then current date stays) or just date (then current time stays).
* <p/>
* If parse fails for whatever reason, nothing is changed.
*
* @param isoDate String with ISO date (date+time, date or just time)
*/
public static synchronized void setIsoDate(String isoDate) {
try {
if (isoDate.indexOf('T') != -1) { // it's date and time (so "classic" ISO timestamp)
long wantedMillis = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(isoDate).getTime();
offset = wantedMillis - millisSinceClassInit() - INIT_MILLIS;
} else if (isoDate.indexOf(':') != -1) { // it's just time we suppose
Calendar calx = Calendar.getInstance();
calx.setTime(new SimpleDateFormat("HH:mm:ss").parse(isoDate));

Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, calx.get(Calendar.HOUR_OF_DAY));
cal.set(Calendar.MINUTE, calx.get(Calendar.MINUTE));
cal.set(Calendar.SECOND, calx.get(Calendar.SECOND));
offset = cal.getTimeInMillis() - millisSinceClassInit() - INIT_MILLIS;
} else { // it must be just date then!
Calendar calx = Calendar.getInstance();
calx.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(isoDate));

Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH, calx.get(Calendar.DAY_OF_MONTH));
cal.set(Calendar.MONTH, calx.get(Calendar.MONTH));
cal.set(Calendar.YEAR, calx.get(Calendar.YEAR));
offset = cal.getTimeInMillis() - millisSinceClassInit() - INIT_MILLIS;
}
mockSystemClass();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* Sets ms offset against current millis (not against real, instead changes current value relatively).
*
* @param offset relative ms offset against "current" millis
*/
public static synchronized void changeOffset(long offset) {
SystemTimeShifter.offset += offset;
mockSystemClass();
}

/**
* Sets ms offset against real millis (rewrites previous value).
*
* @param offset new absolute ms offset against real millis
*/
public static synchronized void setOffset(long offset) {
SystemTimeShifter.offset = offset;
mockSystemClass();
}

/**
* Sets current millis to the specified value.
*
* @param timestamp new value of "current" millis
*/
public static synchronized void setMillis(long timestamp) {
offset = timestamp - INIT_MILLIS;
mockSystemClass();
}

/**
* Resets the whole System time shifter and removes all JMockit stuff. Real system call is restored.
*/
public static synchronized void reset() {
Mockit.tearDownMocks(System.class);
mockInstalled = false;
offset = 0;
System.out.println("Current time millis mock REMOVED");
}

private static void mockSystemClass() {
if (!mockInstalled) {
Mockit.setUpMock(SystemMock.class);
System.out.println("Current time millis mock INSTALLED: " + new Date());
mockInstalled = true;
} else {
System.out.println("Current time millis mock probably INSTALLED previously: " + new Date());
}
}

public static boolean isMockInstalled() {
return mockInstalled;
}

/**
* Handy if you set up the mock by some other means like {@link Mockit#setUpStartupMocks(Object...)}.
*
* @param mockInstalled true if you want to pretend that the mock is already in place (or is/will be installed otherwise)
*/
public static void setMockInstalled(boolean mockInstalled) {
SystemTimeShifter.mockInstalled = mockInstalled;
}

/**
* Returns real time millis based on nano timer difference (not really a call to {@link System#currentTimeMillis()}.
*
* @return real time millis as close as possible
*/
public static long currentRealTimeMillis() {
return INIT_MILLIS + millisSinceClassInit();
}

private static long millisSinceClassInit() {
return (System.nanoTime() - INIT_NANOS) / 1000000;
}

@MockClass(realClass = System.class)
public static class SystemMock {
/**
* Fake current time millis returns value modified by required offset.
*
* @return fake "current" millis
*/
@Mock
public static long currentTimeMillis() {
return INIT_MILLIS + offset + millisSinceClassInit();
}
}
}

+ 0
- 73
wizard-server/src/main/java/org/cobbzilla/wizard/cache/redis/ActivationCodeService.java 파일 보기

@@ -1,73 +0,0 @@
package org.cobbzilla.wizard.cache.redis;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.string.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.cobbzilla.wizard.cache.redis.RedisService.EX;
import static org.cobbzilla.wizard.cache.redis.RedisService.NX;

@Service @Slf4j
public class ActivationCodeService {

@Autowired @Getter @Setter private RedisService redis;

public String peek(String key) { return redis.get_plaintext(key); }

public boolean attempt(String key, String claimant) {
try {
final Long remaining = redis.decr(key);
if (remaining == null || remaining < 0) return false;
redis.lpush(getClaimantsKey(key), claimant);
return true;

} catch (Exception e) {
log.warn("attempt("+key+") error: "+e);
return false;
}
}

public void define (String key, int quantity, long expirationSeconds) {
redis.set_plaintext(key, String.valueOf(quantity), NX, EX, expirationSeconds);
}

public List<String> getClaimants (String key) { return redis.list(getClaimantsKey(key)); }

private String getClaimantsKey(String key) { return key+"_claimed"; }

/**
* @param args [0] = key; [1] = quantity; [2] = expiration (# days); [3] = redis key (optional)
*/
public static void main (final String[] args) {

final RedisService redis = new RedisService();
final String redisKey = (args.length == 4) ? args[3] : null;

redis.setConfiguration(() -> new RedisConfiguration(redisKey));

final ActivationCodeService acService = new ActivationCodeService();
acService.setRedis(redis);

final String key = args[0];

if (args.length > 1) {
final int quantity = Integer.parseInt(args[1]);
final long expirationSeconds = Integer.parseInt(args[2]) * TimeUnit.DAYS.toSeconds(1);

acService.define(key, quantity, expirationSeconds);
System.out.println("successfully defined key: " + key);

} else {
System.out.println("key: " + key);
System.out.println("remaining: " + acService.peek(key));
System.out.println("claimants: " + StringUtil.toString(acService.getClaimants(key), ", "));
}
}

}

+ 41
- 11
wizard-server/src/main/java/org/cobbzilla/wizard/cache/redis/RedisService.java 파일 보기

@@ -73,7 +73,7 @@ public class RedisService {
}

public void reconnect () {
log.debug("marking redis for reconnection...");
if (log.isDebugEnabled()) log.debug("marking redis for reconnection...");
synchronized (redis) {
if (redis.get() != null) {
try { redis.get().disconnect(); } catch (Exception e) {
@@ -87,7 +87,7 @@ public class RedisService {
private Jedis getRedis () {
synchronized (redis) {
if (redis.get() == null) {
log.debug("connecting to redis...");
if (log.isDebugEnabled()) log.debug("connecting to redis...");
redis.set(newJedis());
}
}
@@ -130,6 +130,17 @@ public class RedisService {

public void set(String key, String value) { __set(key, value, 0, MAX_RETRIES); }

public void set_plaintext(String key, String value, String nxxx, String expx, long time) {
__set_plaintext(key, value, nxxx, expx, time, 0, MAX_RETRIES);
}

public void set_plaintext(String key, String value, String expx, long time) {
__set_plaintext(key, value, XX, expx, time, 0, MAX_RETRIES);
__set_plaintext(key, value, NX, expx, time, 0, MAX_RETRIES);
}

public void set_plaintext(String key, String value) { __set_plaintext(key, value, 0, MAX_RETRIES); }

public void setAll(Collection<String> keys, String value, String expx, long time) {
for (String k : keys) set(k, value, expx, time);
}
@@ -164,14 +175,6 @@ public class RedisService {
public Long del(String key) { return __del(key, 0, MAX_RETRIES); }
public Long del_withPrefix(String prefixedKey) { return __del(prefixedKey, 0, MAX_RETRIES, false); }

public void set_plaintext(String key, String value, String nxxx, String expx, long time) {
__set(key, value, nxxx, expx, time, 0, MAX_RETRIES);
}

public void set_plaintext(String key, String value) {
__set(key, value, 0, MAX_RETRIES);
}

public Long sadd(String key, String value) { return sadd(key, new String[]{value}); }
public Long sadd(String key, String[] values) { return __sadd(key, values, 0, MAX_RETRIES); }

@@ -217,18 +220,21 @@ public class RedisService {
}

public String lock(String key, long lockTimeout, long deadlockTimeout) {
if (log.isDebugEnabled()) log.debug("lock("+key+") starting");
key = key + LOCK_SUFFIX;
final String uuid = UUID.randomUUID().toString();
String lockVal = get(key);
final long start = now();
while ((lockVal == null || !lockVal.equals(uuid)) && (now() - start < lockTimeout)) {
set(key, uuid, NX, EX, deadlockTimeout/1000);
if (log.isDebugEnabled()) log.debug("lock("+key+") locked with uuid="+uuid);
lockVal = get(key);
if (log.isDebugEnabled()) log.debug("lock("+key+") after locking with uuid="+uuid+", lockVal="+lockVal);
}
if (lockVal == null || !lockVal.equals(uuid)) {
return die("lock: timeout locking "+key);
}
log.info("lock: LOCKED "+key);
if (log.isDebugEnabled()) log.debug("lock: LOCKED "+key+" = "+lockVal);
return uuid;
}

@@ -372,6 +378,30 @@ public class RedisService {
}
}

private String __set_plaintext(String key, String value, String nxxx, String expx, long time, int attempt, int maxRetries) {
try {
synchronized (redis) {
return getRedis().set(prefix(key), value, nxxx, expx, time);
}
} catch (RuntimeException e) {
if (attempt > maxRetries) throw e;
resetForRetry(attempt, "retrying RedisService.__set_plaintext");
return __set_plaintext(key, value, nxxx, expx, time, attempt + 1, maxRetries);
}
}

private String __set_plaintext(String key, String value, int attempt, int maxRetries) {
try {
synchronized (redis) {
return getRedis().set(prefix(key), value);
}
} catch (RuntimeException e) {
if (attempt > maxRetries) throw e;
resetForRetry(attempt, "retrying RedisService.__set_plaintext");
return __set_plaintext(key, value, attempt+1, maxRetries);
}
}

private Long __lpush(String key, String value, int attempt, int maxRetries) {
try {
synchronized (redis) {


+ 29
- 8
wizard-server/src/main/java/org/cobbzilla/wizard/dao/AbstractCRUDDAO.java 파일 보기

@@ -6,6 +6,7 @@ package org.cobbzilla.wizard.dao;
*/

import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.reflect.ReflectionUtil;
import org.cobbzilla.wizard.api.CrudOperation;
@@ -26,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.*;
@@ -34,6 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
@@ -252,15 +255,22 @@ public abstract class AbstractCRUDDAO<E extends Identifiable>
}
}

public int bulkUpdate(String setField, Object setValue) {
public int bulkUpdate(@NonNull final String setField, @Nullable final Object setValue) {
return bulkUpdate(setField, setValue, (String[]) null, null);
}

public int bulkUpdate(String setField, Object setValue, String whereField, Object whereValue) {
return bulkUpdate(setField, setValue, new String[] {whereField}, new Object[] {whereValue});
public int bulkUpdate(@NonNull final String setField, @Nullable final Object setValue,
@NonNull final String whereField, @Nullable final Object whereValue) {
return bulkUpdate(setField, setValue, new String[] { whereField }, new Object[] { whereValue });
}

public int bulkUpdate(String setField, Object setValue, String[] whereFields, Object[] whereValues) {
public int bulkUpdate(@NonNull final String setField, @Nullable final Object setValue,
@Nullable final String[] whereFields, @Nullable final Object[] whereValues) {
return bulkUpdate(new String[] { setField }, new Object[] { setValue }, whereFields, whereValues);
}

public int bulkUpdate(@NonNull final String[] setFields, @Nullable final Object[] setValues,
@Nullable final String[] whereFields, @Nullable final Object[] whereValues) {
final Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
final String whereClause;
final boolean hasWhereClause = !empty(whereFields);
@@ -282,13 +292,24 @@ public abstract class AbstractCRUDDAO<E extends Identifiable>
if (!empty(whereValues)) return die("bulkUpdate: number of whereFields did not match number of whereValues");
whereClause = "";
}

final Query queryBase;
if (setValue == null) {
queryBase = session.createQuery("UPDATE " + getEntityClass().getSimpleName() + " SET " + setField + " = NULL"+whereClause);
if (empty(setValues)) {
queryBase = session.createQuery("UPDATE " + getEntityClass().getSimpleName()
+ " SET " + String.join(" IS NULL, ", setFields) + " IS NULL"
+ whereClause);
} else {
queryBase = session.createQuery("UPDATE " + getEntityClass().getSimpleName() + " SET " + setField + " = :" + setField+whereClause)
.setParameter(setField, setValue);
final var setFieldsSQLPart = Arrays.stream(setFields)
.map(s -> s + (s == null ? " IS NULL" : " = :" + s))
.collect(Collectors.joining(", "));
queryBase = session.createQuery("UPDATE " + getEntityClass().getSimpleName()
+ " SET " + setFieldsSQLPart
+ whereClause);
for (var i = 0; i < setValues.length; i++) {
if (setValues[i] != null) queryBase.setParameter(setFields[i], setValues[i]);
}
}

final Query query;
if (hasWhereClause) {
Query q = queryBase;


불러오는 중...
취소
저장