mirror of
https://gitee.com/actframework/actframework.git
synced 2024-11-29 18:37:37 +08:00
parent
26dacc4101
commit
23ff13fa93
@ -1,6 +1,7 @@
|
||||
# ActFramework Change Log
|
||||
|
||||
**1.8.29**
|
||||
* Incorrect message returned by `ActContext.i18n(template, args)` #1211
|
||||
* Provide a default HTML page for async controller response #1209
|
||||
* Allow it auto refresh when got 409 error during hot-reloading #1207
|
||||
* "The response has already been started" error #1208
|
||||
|
@ -5,18 +5,18 @@
|
||||
| aaa | 1.5.4 | 1.5.5 | 1.5.5 | 1.5.5 | 1.5.5 | 1.5.5 | 1.6.0 | 1.6.1 | 1.6.1 |
|
||||
| beetl | 1.4.7 | 1.4.8 | 1.5.1 | 1.5.2 | 1.5.3 | 1.5.4 | 1.6.0 | 1.6.1 | 1.6.1 |
|
||||
| beetlsql | 1.5.7 | 1.5.8 | 1.5.9 | 1.5.10 | 1.5.11 | 1.6.0 | 1.7.0 | 1.7.1 | 1.7.1 |
|
||||
| ebean-java7 | 1.7.5 | 1.7.6 | 1.7.6 | 1.7.7 | 1.7.7 | 1.7.8 | 1.7.8 | 1.7.8 | 1.7.8 |
|
||||
| ebean(java8) | 1.7.6 | 1.7.7 | 1.7.7 | 1.7.8 | 1.7.8 | 1.7.9 | 1.7.9 | 1.7.9 | 1.7.9 |
|
||||
| eclipselink(java8) | 1.5.7 | 1.5.8 | 1.5.8 | 1.5.9 | 1.5.9 | 1.6.0 | 1.6.0 | 1.6.0 | 1.6.0 |
|
||||
| excel | 1.5.0 | 1.6.0 | 1.6.1 | 1.6.2 | 1.6.2 | 1.6.2 | 1.7.0 | 1.7.1 | 1.7.1 |
|
||||
| ebean-java7 | 1.7.5 | 1.7.6 | 1.7.6 | 1.7.7 | 1.7.7 | 1.7.8 | 1.7.8 | 1.7.8 | 1.7.9 |
|
||||
| ebean(java8) | 1.7.6 | 1.7.7 | 1.7.7 | 1.7.8 | 1.7.8 | 1.7.9 | 1.7.9 | 1.7.9 | 1.7.10 |
|
||||
| eclipselink(java8) | 1.5.7 | 1.5.8 | 1.5.8 | 1.5.9 | 1.5.9 | 1.6.0 | 1.6.0 | 1.6.0 | 1.6.1 |
|
||||
| excel | 1.5.0 | 1.6.0 | 1.6.1 | 1.6.2 | 1.6.2 | 1.6.2 | 1.7.0 | 1.7.1 | 1.7.2 |
|
||||
| freemarker | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.7 | 1.3.7 |
|
||||
| hibernate | 1.5.6 | 1.5.7 | 1.5.7 | 1.5.8 | 1.5.8 | 1.6.0 | 1.6.0 | 1.6.0 | 1.6.0 |
|
||||
| hibernate | 1.5.6 | 1.5.7 | 1.5.7 | 1.5.8 | 1.5.8 | 1.6.0 | 1.6.0 | 1.6.0 | 1.6.1 |
|
||||
| jax-rs(java8) | 1.0.5 | 1.0.5 | 1.0.5 | 1.0.5 | 1.0.5 | 1.0.5 | 1.0.5 | 1.0.5 | 1.0.5 |
|
||||
| jpa-common | 1.5.6 | 1.5.7 | 1.5.7 | 1.5.8 | 1.5.8 | 1.6.0 | 1.6.0 | 1.6.0 | 1.6.0 |
|
||||
| morphia | 1.6.6 | 1.6.7 | 1.6.7 | 1.7.1 | 1.7.2 | 1.7.2 | 1.7.2 | 1.7.2 | 1.7.2 |
|
||||
| jpa-common | 1.5.6 | 1.5.7 | 1.5.7 | 1.5.8 | 1.5.8 | 1.6.0 | 1.6.0 | 1.6.0 | 1.6.1 |
|
||||
| morphia | 1.6.6 | 1.6.7 | 1.6.7 | 1.7.1 | 1.7.2 | 1.7.2 | 1.7.2 | 1.7.2 | 1.7.3 |
|
||||
| mustache(java8) | 1.4.5 | 1.4.5 | 1.4.6 | 1.4.6 | 1.4.6 | 1.4.6 | 1.4.6 | 1.4.6 | 1.4.6 |
|
||||
| social | 0.12.5 | 0.12.5 | 0.12.6 | 0.12.6 | 0.12.6 | 0.12.6 | 0.12.6 | 0.12.6 | 0.12.6 |
|
||||
| sql-common | 1.4.4 | 1.4.5 | 1.4.5 | 1.4.6 | 1.4.6 | 1.5.0 | 1.5.0 | 1.5.0 | 1.5.0 |
|
||||
| sql-common | 1.4.4 | 1.4.5 | 1.4.5 | 1.4.6 | 1.4.6 | 1.5.0 | 1.5.0 | 1.5.0 | 1.5.1 |
|
||||
| storage(java8) | 0.13.5 | 0.13.6 | 0.13.6 | 0.13.6 | 0.13.6 | 0.13.6 | 0.14.0 | 0.14.0 | 0.14.0 |
|
||||
| thymeleaf | 1.3.5 | 1.3.5 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 |
|
||||
| velocity | 1.3.5 | 1.3.5 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.6 | 1.3.7 | 1.3.7 |
|
||||
|
BIN
doc/img/gh1209a.gif
Normal file
BIN
doc/img/gh1209a.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
@ -0,0 +1,59 @@
|
||||
package act.handler.builtin.controller;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* ACT Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 ActFramework
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import act.Act;
|
||||
import act.util.SimpleProgressGauge;
|
||||
|
||||
/**
|
||||
* Used for track async request handler method progress
|
||||
*/
|
||||
public class ControllerProgressGauge extends SimpleProgressGauge {
|
||||
|
||||
@Override
|
||||
public void fail(String error) {
|
||||
this.error = error;
|
||||
// do not trigger update event yet
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepBy(int steps) {
|
||||
currentSteps += steps;
|
||||
if (!isDone()) {
|
||||
triggerUpdateEvent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepTo(int steps) {
|
||||
if (currentSteps != steps) {
|
||||
currentSteps = steps;
|
||||
if (!isDone()) {
|
||||
triggerUpdateEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void commitFinalState() {
|
||||
triggerUpdateEvent(true);
|
||||
Act.LOGGER.info("!!!!!!!!!!!!!!!!! final state committed");
|
||||
}
|
||||
}
|
@ -73,6 +73,7 @@ import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.validation.ConstraintViolation;
|
||||
|
||||
@ -655,19 +656,27 @@ public class ReflectedHandlerInvoker<M extends HandlerMethodMetaInfo> extends Lo
|
||||
if (async) {
|
||||
final JobManager jobManager = context.app().jobManager();
|
||||
final String jobId = jobManager.randomJobId();
|
||||
final String reqInfo = context.req().toString();
|
||||
jobManager.prepare(jobId, new TrackableWorker() {
|
||||
@Override
|
||||
protected void run(ProgressGauge progressGauge) {
|
||||
if (isTraceEnabled()) {
|
||||
trace("about invoking request handler[%s] asynchronously: %s", reqInfo, jobId);
|
||||
}
|
||||
try {
|
||||
Object o = invoke(context, controller, params);
|
||||
if (null == o) {
|
||||
o = "done";
|
||||
}
|
||||
jobManager.cacheResult(jobId, o, method);
|
||||
context.progress().commitFinalState();
|
||||
} catch (Exception e) {
|
||||
warn(e, "Error executing async handler: " + method);
|
||||
context.progress().fail(e.getMessage());
|
||||
jobManager.cacheResult(jobId, C.Map("failed", true), method);
|
||||
String errMsg = S.buffer("Error handling request: ").append(reqInfo).toString();
|
||||
warn(e, errMsg);
|
||||
ControllerProgressGauge gauge = context.progress();
|
||||
gauge.fail(errMsg);
|
||||
jobManager.cacheResult(jobId, C.Map("error", e.getLocalizedMessage()), method);
|
||||
gauge.commitFinalState();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -675,12 +684,14 @@ public class ReflectedHandlerInvoker<M extends HandlerMethodMetaInfo> extends Lo
|
||||
WebSocketConnectionManager wscm = app.getInstance(WebSocketConnectionManager.class);
|
||||
wscm.subscribe(context.session(), SimpleProgressGauge.wsJobProgressTag(jobId));
|
||||
jobManager.now(jobId);
|
||||
if (context.req().accept() == H.Format.HTML) {
|
||||
boolean isHtml = context.req().accept() == H.Format.HTML;
|
||||
if (isHtml) {
|
||||
context.templatePath("/act/asyncJob.html");
|
||||
context.renderArg("jobId", jobId);
|
||||
return RenderTemplate.get();
|
||||
} else {
|
||||
return new RenderJSON(C.Map("jobId", jobId, "jobResultUrl", S.concat("/~/jobs/", jobId, "/result")));
|
||||
String resultUrl = context.router().fullUrl("/~/jobs/%s/result", jobId);
|
||||
return new RenderJSON(C.Map("jobId", jobId, "resultUrl", resultUrl));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,21 +88,26 @@ public class I18n {
|
||||
bundleName = act_messages.class.getName();
|
||||
}
|
||||
ResourceBundle bundle;
|
||||
String msg = msgId;
|
||||
try {
|
||||
bundle = ResourceBundle.getBundle(bundleName, $.requireNotNull(locale), Act.app().classLoader());
|
||||
} catch (MissingResourceException e) {
|
||||
return msgId;
|
||||
}
|
||||
String msg = msgId;
|
||||
if (ignoreError) {
|
||||
if (bundle.containsKey(msgId)) {
|
||||
msg = bundle.getString(msgId);
|
||||
if (ignoreError) {
|
||||
logger.warn("Cannot find bundle: %s", bundleName);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
msg = bundle.getString(msgId);
|
||||
} catch (MissingResourceException e) {
|
||||
logger.warn("Cannot find i18n message key: %s", msgId);
|
||||
bundle = null;
|
||||
}
|
||||
if (null != bundle) {
|
||||
if (ignoreError) {
|
||||
if (bundle.containsKey(msgId)) {
|
||||
msg = bundle.getString(msgId);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
msg = bundle.getString(msgId);
|
||||
} catch (MissingResourceException e) {
|
||||
logger.warn("Cannot find i18n message key: %s", msgId);
|
||||
}
|
||||
}
|
||||
}
|
||||
int len = args.length;
|
||||
@ -116,6 +121,13 @@ public class I18n {
|
||||
resolvedArgs[i] = arg;
|
||||
}
|
||||
}
|
||||
if (msg.contains("%")) {
|
||||
// try String.format first
|
||||
String result = S.fmt(msg, resolvedArgs);
|
||||
if (S.neq(result, msg)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
MessageFormat formatter = new MessageFormat(msg, locale);
|
||||
msg = formatter.format(resolvedArgs);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import act.app.ActionContext;
|
||||
import act.app.App;
|
||||
import act.cli.CliContext;
|
||||
import act.conf.AppConfig;
|
||||
import act.handler.builtin.controller.ControllerProgressGauge;
|
||||
import act.i18n.I18n;
|
||||
import act.mail.MailerContext;
|
||||
import act.view.Template;
|
||||
@ -277,7 +278,7 @@ public interface ActContext<CTX_TYPE extends ActContext> extends ParamValueProvi
|
||||
private Locale locale;
|
||||
private int fieldOutputVarCount;
|
||||
private boolean noTemplateCache;
|
||||
private volatile ProgressGauge progress;
|
||||
private volatile ControllerProgressGauge progress;
|
||||
protected String jobId;
|
||||
private Method handlerMethod;
|
||||
private Method currentMethod;
|
||||
@ -720,11 +721,11 @@ public interface ActContext<CTX_TYPE extends ActContext> extends ParamValueProvi
|
||||
app().jobManager().setJobProgressGauge(jobId, progress());
|
||||
}
|
||||
|
||||
public ProgressGauge progress() {
|
||||
public ControllerProgressGauge progress() {
|
||||
if (null == progress) {
|
||||
synchronized (this) {
|
||||
if (null == progress) {
|
||||
progress = new SimpleProgressGauge();
|
||||
progress = new ControllerProgressGauge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,12 +83,12 @@ public class SimpleProgressGauge extends DestroyableBase implements ProgressGaug
|
||||
|
||||
private String id;
|
||||
private boolean markedAsDown;
|
||||
private int maxHint;
|
||||
private int currentSteps;
|
||||
private String error;
|
||||
protected int maxHint;
|
||||
protected int currentSteps;
|
||||
protected String error;
|
||||
private Map<String, Object> payload = new HashMap<>();
|
||||
private transient int percent;
|
||||
private ProgressGauge delegate;
|
||||
protected ProgressGauge delegate;
|
||||
private List<Listener> listeners = new ArrayList<>();
|
||||
private ReadWriteLock listenerListLock = new ReentrantReadWriteLock();
|
||||
|
||||
@ -161,12 +161,7 @@ public class SimpleProgressGauge extends DestroyableBase implements ProgressGaug
|
||||
|
||||
@Override
|
||||
public void step() {
|
||||
if (null != delegate) {
|
||||
delegate.step();
|
||||
} else {
|
||||
currentSteps++;
|
||||
triggerUpdateEvent();
|
||||
}
|
||||
this.stepBy(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -310,11 +305,11 @@ public class SimpleProgressGauge extends DestroyableBase implements ProgressGaug
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
private void triggerUpdateEvent() {
|
||||
protected void triggerUpdateEvent() {
|
||||
triggerUpdateEvent(false);
|
||||
}
|
||||
|
||||
private void triggerUpdateEvent(boolean forceTriggerEvent) {
|
||||
protected void triggerUpdateEvent(boolean forceTriggerEvent) {
|
||||
if (forceTriggerEvent || percentageChanged()) {
|
||||
Lock lock = listenerListLock.readLock();
|
||||
lock.lock();
|
||||
|
@ -15,7 +15,7 @@
|
||||
font-family: "Noto Sans", Tahoma, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
#test-result {
|
||||
#job-result {
|
||||
display: none;
|
||||
}
|
||||
#in-progress {
|
||||
@ -120,10 +120,17 @@
|
||||
font-weight: bold;
|
||||
margin-top: 4px;
|
||||
}
|
||||
pre#result {
|
||||
#result-detail {
|
||||
margin-top: 30px;
|
||||
padding: 10px;
|
||||
border: 1px solid #aaa;
|
||||
font-family: "Envy Code R", "Fira Code", "Source Code Pro Semibold", Monaco, Courier, monospace;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
#result-detail.error {
|
||||
border-color: #ff4848;
|
||||
color: #ff4848;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -136,44 +143,14 @@
|
||||
<div id="progress"></div>
|
||||
<div id="progress-text"></div>
|
||||
</div>
|
||||
<ul id="error-scenario-list" class="FAIL">
|
||||
</ul>
|
||||
</div>
|
||||
<div id="test-result">
|
||||
<div id="job-result">
|
||||
<h1>Job Result</h1>
|
||||
<div class="version">@(_app.name().capFirst())-@(_app.version().getVersion())</div>
|
||||
<pre id="result" style="margin-top: 30px"></pre>
|
||||
<pre id="result-detail"></pre>
|
||||
</div>
|
||||
<script>
|
||||
var getResultHandle = false
|
||||
function testCallback(data) {
|
||||
var ws = $.createWebSocket('/~/ws/jobs/@jobId/progress', function(e) {
|
||||
alert("Unknown error")
|
||||
})
|
||||
ws.onmessage = function(msg) {
|
||||
if (getResultHandle) {
|
||||
clearTimeout(getResultHandle)
|
||||
getResultHandle = false
|
||||
}
|
||||
if (!msg.data) {
|
||||
alert("no job progress")
|
||||
return
|
||||
}
|
||||
var data = JSON.parse(msg.data)
|
||||
var gauge = data.act_job_progress
|
||||
if (gauge.done || gauge.isFailed) {
|
||||
$('#progress-text').text('100/100')
|
||||
$('#progress').width(800)
|
||||
setTimeout(getResult, 100)
|
||||
getResult()
|
||||
} else {
|
||||
$('#progress-text').text((gauge.currentSteps + 1) + '/' + (gauge.maxHint))
|
||||
$('#progress').width(gauge.progressPercent * 800 / 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
testCallback()
|
||||
function getResult() {
|
||||
function getResult(fail) {
|
||||
$.getJSON('/~/jobs/@jobId/result', function(data) {
|
||||
if (data.failed) {
|
||||
favicon = '/~/asset/img/test-failure.ico?v=@act.Act.VERSION.getBuildNumber()'
|
||||
@ -182,12 +159,30 @@
|
||||
}
|
||||
$('#favicon-link').attr('href', favicon)
|
||||
var result = JSON.stringify(data, null, 2)
|
||||
$('#result').text(result)
|
||||
$('#result-detail').text(result)
|
||||
$('#in-progress').hide()
|
||||
$('#test-result').show()
|
||||
if (fail) {
|
||||
$('#result-detail').addClass('error')
|
||||
}
|
||||
$('#job-result').show()
|
||||
})
|
||||
}
|
||||
getResultHandle = setTimeout(getResult, 1000)
|
||||
function monitorProgress() {
|
||||
var ws = $.createWebSocket('/~/ws/jobs/@jobId/progress')
|
||||
ws.onmessage = function(msg) {
|
||||
var data = JSON.parse(msg.data)
|
||||
var gauge = data.act_job_progress
|
||||
if (gauge.done || gauge.failed) {
|
||||
$('#progress-text').text('100/100')
|
||||
$('#progress').width(800)
|
||||
getResult(gauge.failed)
|
||||
} else {
|
||||
$('#progress-text').text((gauge.currentSteps + 1) + '/' + (gauge.maxHint))
|
||||
$('#progress').width(gauge.progressPercent * 800 / 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
monitorProgress()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
20
testapps/GHIssues/src/main/java/ghissues/Gh1211.java
Normal file
20
testapps/GHIssues/src/main/java/ghissues/Gh1211.java
Normal file
@ -0,0 +1,20 @@
|
||||
package ghissues;
|
||||
|
||||
import act.app.ActionContext;
|
||||
import act.controller.annotation.UrlContext;
|
||||
import org.osgl.mvc.annotation.GetAction;
|
||||
|
||||
@UrlContext("1211")
|
||||
public class Gh1211 extends BaseController {
|
||||
|
||||
@GetAction
|
||||
public String test(ActionContext context) {
|
||||
return context.i18n("hello %s", "world");
|
||||
}
|
||||
|
||||
@GetAction("2")
|
||||
public String test2(ActionContext context) {
|
||||
return context.i18n("hello {0}", "world");
|
||||
}
|
||||
|
||||
}
|
12
testapps/GHIssues/src/main/resources/test/scenarios/1211.yml
Normal file
12
testapps/GHIssues/src/main/resources/test/scenarios/1211.yml
Normal file
@ -0,0 +1,12 @@
|
||||
Scenario(1211):
|
||||
interactions:
|
||||
- description: test case 1
|
||||
request:
|
||||
get: 1211
|
||||
response:
|
||||
result: hello world
|
||||
- description: test case 2
|
||||
request:
|
||||
get: 1211/2
|
||||
response:
|
||||
result: hello world
|
Loading…
Reference in New Issue
Block a user