This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch WW-5080-plain-result in repository https://gitbox.apache.org/repos/asf/struts.git
commit f02e0d2b815aff49ce88b5e0f0296733586b28d2 Author: Lukasz Lenart <lukaszlen...@apache.org> AuthorDate: Mon Jun 29 08:16:48 2020 +0200 WW-5080 Defines a new result type plain to use directly with Java code --- .../org/apache/struts2/result/PlainResult.java | 64 ++++++++++++ .../apache/struts2/result/plain/BodyWriter.java | 41 ++++++++ .../struts2/result/plain/DateHttpHeader.java | 38 +++++++ .../apache/struts2/result/plain/HttpCookies.java | 39 ++++++++ .../apache/struts2/result/plain/HttpHeader.java | 27 +++++ .../apache/struts2/result/plain/HttpHeaders.java | 58 +++++++++++ .../apache/struts2/result/plain/IntHttpHeader.java | 39 ++++++++ .../struts2/result/plain/ResponseBuilder.java | 111 +++++++++++++++++++++ .../struts2/result/plain/StringHttpHeader.java | 39 ++++++++ .../org/apache/struts2/result/PlainResultTest.java | 99 ++++++++++++++++++ 10 files changed, 555 insertions(+) diff --git a/core/src/main/java/org/apache/struts2/result/PlainResult.java b/core/src/main/java/org/apache/struts2/result/PlainResult.java new file mode 100644 index 0000000..3cbeb09 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/PlainResult.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result; + +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.Result; +import org.apache.struts2.StrutsException; +import org.apache.struts2.result.plain.HttpHeader; +import org.apache.struts2.result.plain.ResponseBuilder; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +public interface PlainResult extends Result { + + @Override + default void execute(ActionInvocation invocation) throws Exception { + ResponseBuilder builder = new ResponseBuilder(); + write(builder); + + HttpServletResponse response = invocation.getInvocationContext().getServletResponse(); + + if (response.isCommitted()) { + throw new StrutsException("Http response already committed, cannot modify it!"); + } + + for (HttpHeader<String> header : builder.getStringHeaders()) { + response.addHeader(header.getName(), header.getValue()); + } + for (HttpHeader<Long> header : builder.getDateHeaders()) { + response.addDateHeader(header.getName(), header.getValue()); + } + for (HttpHeader<Integer> header : builder.getIntHeaders()) { + response.addIntHeader(header.getName(), header.getValue()); + } + + for (Cookie cookie : builder.getCookies()) { + response.addCookie(cookie); + } + + response.getWriter().write(builder.getBody()); + response.flushBuffer(); + } + + void write(ResponseBuilder response); + +} + diff --git a/core/src/main/java/org/apache/struts2/result/plain/BodyWriter.java b/core/src/main/java/org/apache/struts2/result/plain/BodyWriter.java new file mode 100644 index 0000000..ff22769 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/BodyWriter.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +import java.io.StringWriter; + +class BodyWriter { + + private final StringWriter body = new StringWriter(); + + public BodyWriter write(String out) { + body.write(out); + return this; + } + + public BodyWriter writeLine(String out) { + body.write(out); + body.write("\n"); + return this; + } + + public String getBody() { + return body.toString(); + } +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/DateHttpHeader.java b/core/src/main/java/org/apache/struts2/result/plain/DateHttpHeader.java new file mode 100644 index 0000000..906860b --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/DateHttpHeader.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +class DateHttpHeader implements HttpHeader<Long> { + + private final String name; + private final Long value; + + public DateHttpHeader(String name, Long value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public Long getValue() { + return value; + } +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/HttpCookies.java b/core/src/main/java/org/apache/struts2/result/plain/HttpCookies.java new file mode 100644 index 0000000..22c105b --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/HttpCookies.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +import javax.servlet.http.Cookie; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class HttpCookies { + + private final List<Cookie> cookies = new ArrayList<>(); + + public HttpCookies add(String name, String value) { + cookies.add(new Cookie(name, value)); + return this; + } + + public List<Cookie> getCookies() { + return Collections.unmodifiableList(cookies); + } + +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/HttpHeader.java b/core/src/main/java/org/apache/struts2/result/plain/HttpHeader.java new file mode 100644 index 0000000..90de858 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/HttpHeader.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +public interface HttpHeader<T> { + + String getName(); + + T getValue(); + +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/HttpHeaders.java b/core/src/main/java/org/apache/struts2/result/plain/HttpHeaders.java new file mode 100644 index 0000000..31715c6 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/HttpHeaders.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class HttpHeaders { + + private final List<HttpHeader<String>> stringHeaders = new ArrayList<>(); + private final List<HttpHeader<Long>> dateHeaders = new ArrayList<>(); + private final List<HttpHeader<Integer>> intHeaders = new ArrayList<>(); + + public HttpHeaders add(String name, String value) { + stringHeaders.add(new StringHttpHeader(name, value)); + return this; + } + + public HttpHeaders add(String name, Long value) { + dateHeaders.add(new DateHttpHeader(name, value)); + return this; + } + + public HttpHeaders add(String name, Integer value) { + intHeaders.add(new IntHttpHeader(name, value)); + return this; + } + + public List<HttpHeader<String>> getStringHeaders() { + return Collections.unmodifiableList(stringHeaders); + } + + public List<HttpHeader<Long>> getDateHeaders() { + return Collections.unmodifiableList(dateHeaders); + } + + public List<HttpHeader<Integer>> getIntHeaders() { + return Collections.unmodifiableList(intHeaders); + } + +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/IntHttpHeader.java b/core/src/main/java/org/apache/struts2/result/plain/IntHttpHeader.java new file mode 100644 index 0000000..c190617 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/IntHttpHeader.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +class IntHttpHeader implements HttpHeader<Integer> { + + private final String name; + private final Integer value; + + public IntHttpHeader(String name, Integer value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public Integer getValue() { + return value; + } + +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/ResponseBuilder.java b/core/src/main/java/org/apache/struts2/result/plain/ResponseBuilder.java new file mode 100644 index 0000000..5e1ae20 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/ResponseBuilder.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +import javax.servlet.http.Cookie; + +public class ResponseBuilder { + + public static final String CONTENT_TYPE = "Content-Type"; + + public static final String TEXT_PLAIN = "text/plain"; + public static final String TEXT_HTML = "text/html"; + public static final String APPLICATION_JSON = "application/json"; + + private final BodyWriter body; + private final HttpHeaders headers; + private final HttpCookies cookies; + + public ResponseBuilder() { + this.body = new BodyWriter(); + this.headers = new HttpHeaders().add(CONTENT_TYPE, TEXT_PLAIN + "; charset=UTF-8"); + this.cookies = new HttpCookies(); + } + + public ResponseBuilder write(String out) { + body.write(out); + return this; + } + + public ResponseBuilder writeLine(String out) { + body.writeLine(out); + return this; + } + + public ResponseBuilder withHeader(String name, String value) { + headers.add(name, value); + return this; + } + + public ResponseBuilder withHeader(String name, Long value) { + headers.add(name, value); + return this; + } + + public ResponseBuilder withHeader(String name, Integer value) { + headers.add(name, value); + return this; + } + + public ResponseBuilder withContentTypeTextPlain() { + headers.add(CONTENT_TYPE, TEXT_PLAIN + "; charset=UTF-8"); + return this; + } + + public ResponseBuilder withContentTypeTextHtml() { + headers.add(CONTENT_TYPE, TEXT_HTML + "; charset=UTF-8"); + return this; + } + + public ResponseBuilder withContentTypeJson() { + headers.add(CONTENT_TYPE, APPLICATION_JSON); + return this; + } + + public ResponseBuilder withContentType(String contentType) { + headers.add(CONTENT_TYPE, contentType); + return this; + } + + public ResponseBuilder withCookie(String name, String value) { + cookies.add(name, value); + return this; + } + + public Iterable<HttpHeader<String>> getStringHeaders() { + return headers.getStringHeaders(); + } + + public Iterable<HttpHeader<Long>> getDateHeaders() { + return headers.getDateHeaders(); + } + + public Iterable<HttpHeader<Integer>> getIntHeaders() { + return headers.getIntHeaders(); + } + + public Iterable<Cookie> getCookies() { + return cookies.getCookies(); + } + + public String getBody() { + return body.getBody(); + } + +} diff --git a/core/src/main/java/org/apache/struts2/result/plain/StringHttpHeader.java b/core/src/main/java/org/apache/struts2/result/plain/StringHttpHeader.java new file mode 100644 index 0000000..e4e3e03 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/result/plain/StringHttpHeader.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result.plain; + +class StringHttpHeader implements HttpHeader<String> { + + private final String name; + private final String value; + + public StringHttpHeader(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + +} diff --git a/core/src/test/java/org/apache/struts2/result/PlainResultTest.java b/core/src/test/java/org/apache/struts2/result/PlainResultTest.java new file mode 100644 index 0000000..9e53cee --- /dev/null +++ b/core/src/test/java/org/apache/struts2/result/PlainResultTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.result; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.mock.MockActionInvocation; +import org.apache.struts2.StrutsInternalTestCase; +import org.springframework.mock.web.MockHttpServletResponse; + +public class PlainResultTest extends StrutsInternalTestCase { + + private MockHttpServletResponse response; + private MockActionInvocation invocation; + + public void testWritePlainText() throws Exception { + PlainResult result = (PlainResult) response -> + response.write("test").withContentTypeTextPlain(); + + result.execute(invocation); + + assertEquals("test", response.getContentAsString()); + assertEquals("text/plain; charset=UTF-8", response.getContentType()); + } + + public void testWritePlainHtml() throws Exception { + PlainResult result = (PlainResult) response -> + response.write("<b>test</b>").withContentTypeTextHtml(); + + result.execute(invocation); + + assertEquals("<b>test</b>", response.getContentAsString()); + assertEquals("text/html; charset=UTF-8", response.getContentType()); + } + + public void testWriteJson() throws Exception { + PlainResult result = (PlainResult) response -> + response.write("{ 'value': 'test' }").withContentTypeJson(); + + result.execute(invocation); + + assertEquals("{ 'value': 'test' }", response.getContentAsString()); + assertEquals("application/json", response.getContentType()); + } + + public void testWriteContentTypeCsvWithCookie() throws Exception { + PlainResult result = (PlainResult) response -> + response.writeLine("name;value") + .withContentType("text/csv") + .withCookie("X-Test", "test") + .writeLine("line;1") + .write("line;2"); + + result.execute(invocation); + + assertEquals("name;value\nline;1\nline;2", response.getContentAsString()); + assertEquals("text/csv", response.getContentType()); + } + + public void testHeaders() throws Exception { + PlainResult result = (PlainResult) response -> + response.withHeader("X-String", "test") + .withHeader("X-Date", 0L) + .withHeader("X-Number", 100) + .write(""); + + result.execute(invocation); + + assertEquals("", response.getContentAsString()); + assertEquals("text/plain; charset=UTF-8", response.getContentType()); + assertEquals("test", response.getHeader("X-String")); + assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeader("X-Date")); + assertEquals("100", response.getHeader("X-NUmber")); + } + + public void setUp() throws Exception { + super.setUp(); + invocation = new MockActionInvocation(); + response = new MockHttpServletResponse(); + invocation.setInvocationContext(ActionContext.getContext()); + + ActionContext.getContext().withServletResponse(response).withActionInvocation(invocation); + } +} \ No newline at end of file