原文:Mkyong
Java 正则表达式示例
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/java-regular-expression-examples/
Java 8 流和正则表达式示例。
Note
Learn the basic regular expression at Wikipedia
1.字符串匹配(正则表达式)
1.1 本例中,检查字符串是否为数字。
JavaRegEx1.java
package com.mkyong.regex;
import java.util.Arrays;
import java.util.List;
public class JavaRegEx1 {
public static void main(String[] args) {
List<String> numbers = Arrays.asList("1", "20", "A1", "333", "A2A211");
for (String number : numbers) {
if (number.matches("\\d+")) {
System.out.println(number); // 1, 20, 333
}
}
// Java 8 stream example
numbers.stream()
.filter(x -> x.matches("\\d+"))
.forEach(System.out::println);
}
}
输出
1
20
333
1
20
333
2.String.replaceAll(正则表达式,替换)
2.1 本例用#
替换所有数字
JavaRegEx2.java
package com.mkyong.regex;
import java.util.Arrays;
import java.util.List;
public class JavaRegEx2 {
public static void main(String[] args) {
List<String> numbers = Arrays.asList("1", "20", "A1", "333", "A2A211");
for (String number : numbers) {
System.out.println(number.replaceAll("\\d", "#"));
}
// Java 8 stream example
numbers.stream()
.map(x -> x.replaceAll("\\d", "#"))
.forEach(System.out::println);
}
}
输出
#
##
A#
###
A#A###
#
##
A#
###
A#A###
3.模式和匹配器
3.1 从字符串列表中查找所有数字。
JavaRegEx3.java
package com.mkyong.regex;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavaRegEx3 {
public static void main(String[] args) {
List<String> numbers = Arrays.asList("1", "20", "A1", "333", "A2A211");
Pattern pattern = Pattern.compile("\\d+");
for (String number : numbers) {
Matcher matcher = pattern.matcher(number);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
}
}
输出
1
20
1
333
2
211
3.2 对于 Java 8 流,首先我们尝试这样转换:
numbers.stream()
.map(x -> pattern.matcher(x))
.filter(Matcher::find) // A2A211, will it loop?
.map(x -> x.group())
.forEach(x -> System.out.println(x));
输出,最后一个 211 不见了?
1
20
1
333
2
该流不能循环使用.filter
来获取所有的组,我们需要一个自定义的Spliterators
hack
JavaRegEx4.java
package com.mkyong.regex;
import java.util.Arrays;
import java.util.List;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.StreamSupport;
public class JavaRegEx4 {
public static void main(String[] args) {
List<String> numbers = Arrays.asList("1", "20", "A1", "333", "A2A211");
Pattern pattern = Pattern.compile("\\d+");
numbers.stream()
.flatMap(x ->
StreamSupport.stream(new MatchItr(pattern.matcher(x)), false))
.forEach(x -> System.out.println(x));
}
final static class MatchItr extends Spliterators.AbstractSpliterator<String> {
private final Matcher matcher;
MatchItr(Matcher m) {
super(m.regionEnd() - m.regionStart(), ORDERED | NONNULL);
matcher = m;
}
public boolean tryAdvance(Consumer<? super String> action) {
if (!matcher.find()) return false;
action.accept(matcher.group());
return true;
}
}
}
输出
1
20
1
333
2
211
4.Java 9,Scanner.findAll(regex)
4.1 Java 9,我们可以使用Scanner.findAll(regex)
返回一个匹配结果流,匹配提供的正则表达式。
Scanner scan = new Scanner("A2A211");
List<String> collect = scan
.findAll("\\d+")
.map(m -> m.group())
.collect(Collectors.toList());
collect.forEach(x -> System.out.println(x));
输出
2
211
最终版本,用 Java 9。
JavaRegEx5.java
package com.mkyong.regex;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class JavaRegEx5 {
public static void main(String[] args) {
List<String> numbers = Arrays.asList("1", "20", "A1", "333", "A2A211");
Pattern pattern = Pattern.compile("\\d+");
List<String> collect = numbers.stream()
.map(x -> new Scanner(x).findAll(pattern)
.map(m -> m.group())
.collect(Collectors.toList())
)
.flatMap(List::stream)
.collect(Collectors.toList());
collect.forEach(x -> System.out.println(x));
}
}
输出
1
20
1
333
2
211
参考
Java–在远程服务器上运行 shell 脚本
本文展示了如何使用 JSch 库通过 SSH 在远程服务器上运行或执行 shell 脚本。
pom.xml
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
1.运行远程 Shell 脚本
这个 Java 示例使用 JSch 通过 SSH 登录一个远程服务器(使用密码),并运行一个 shell 脚本hello.sh
。
1.1 下面是一个远程服务器中的简单 shell 脚本,IP 地址是1.1.1.1
。
hello.sh
#! /bin/sh
echo "hello $1\n";
分配了执行权限。
Terminal
$ chmod +x hello.sh
1.2 在本地,我们可以使用下面的代码在远程服务器上运行或执行上面的 shell 脚本。
RunRemoteScript.java
package com.mkyong.io.howto;
import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
public class RunRemoteScript {
private static final String REMOTE_HOST = "1.1.1.1";
private static final String USERNAME = "";
private static final String PASSWORD = "";
private static final int REMOTE_PORT = 22;
private static final int SESSION_TIMEOUT = 10000;
private static final int CHANNEL_TIMEOUT = 5000;
public static void main(String[] args) {
String remoteShellScript = "/root/hello.sh";
Session jschSession = null;
try {
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
// not recommend, uses jsch.setKnownHosts
//jschSession.setConfig("StrictHostKeyChecking", "no");
// authenticate using password
jschSession.setPassword(PASSWORD);
// 10 seconds timeout session
jschSession.connect(SESSION_TIMEOUT);
ChannelExec channelExec = (ChannelExec) jschSession.openChannel("exec");
// run a shell script
channelExec.setCommand("sh " + remoteShellScript + " mkyong");
// display errors to System.err
channelExec.setErrStream(System.err);
InputStream in = channelExec.getInputStream();
// 5 seconds timeout channel
channelExec.connect(CHANNEL_TIMEOUT);
// read the result from remote server
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) break;
System.out.print(new String(tmp, 0, i));
}
if (channelExec.isClosed()) {
if (in.available() > 0) continue;
System.out.println("exit-status: "
+ channelExec.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channelExec.disconnect();
} catch (JSchException | IOException e) {
e.printStackTrace();
} finally {
if (jschSession != null) {
jschSession.disconnect();
}
}
}
}
输出
Terminal
hello mkyong
exit-status: 0
注意
对于 JSch UnknownHostKey
异常,请将远程服务器 ip 添加到.ssh/known_hosts
中,读取这个。
2.运行远程命令
2.1 下面的例子与上面的例子#1 非常相似。相反,它使用私钥id_rsa
来 SSH 登录远程服务器,确保远程服务器正确配置了公钥。
jsch.addIdentity("/home/mkyong/.ssh/id_rsa");
2.2 并且我们把命令改成了ls
,剩下的代码都是一样的。
channelExec.setCommand("ls -lsah");
2.3 这个 Java 示例运行一个远程服务器命令ls -lsah
来显示目录列表。
RunRemoteCommand.java
package com.mkyong.io.howto;
import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
public class RunRemoteCommand {
private static final String REMOTE_HOST = "1.1.1.1";
private static final String USERNAME = "";
private static final int REMOTE_PORT = 22;
private static final int SESSION_TIMEOUT = 10000;
private static final int CHANNEL_TIMEOUT = 5000;
public static void main(String[] args) {
Session jschSession = null;
try {
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
// not recommend, uses jsch.setKnownHosts
//jschSession.setConfig("StrictHostKeyChecking", "no");
// authenticate using private key
jsch.addIdentity("/home/mkyong/.ssh/id_rsa");
// 10 seconds timeout session
jschSession.connect(SESSION_TIMEOUT);
ChannelExec channelExec = (ChannelExec) jschSession.openChannel("exec");
// Run a command
channelExec.setCommand("ls -lsah");
// display errors to System.err
channelExec.setErrStream(System.err);
InputStream in = channelExec.getInputStream();
// 5 seconds timeout channel
channelExec.connect(CHANNEL_TIMEOUT);
// read the result from remote server
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) break;
System.out.print(new String(tmp, 0, i));
}
if (channelExec.isClosed()) {
if (in.available() > 0) continue;
System.out.println("exit-status: "
+ channelExec.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channelExec.disconnect();
} catch (JSchException | IOException e) {
e.printStackTrace();
} finally {
if (jschSession != null) {
jschSession.disconnect();
}
}
}
}
输出
Terminal
otal 48K
4.0K drwx------ 6 root root 4.0K Aug 4 07:57 .
4.0K drwxr-xr-x 22 root root 4.0K Sep 11 2019 ..
8.0K -rw------- 1 root root 5.5K Aug 3 08:50 .bash_history
4.0K -rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
4.0K drwx------ 2 root root 4.0K Dec 15 2019 .cache
4.0K drwx------ 2 root root 4.0K Dec 15 2019 .docker
4.0K -rwxr-xr-x 1 root root 31 Aug 4 07:54 hello.sh
4.0K -rw-r--r-- 1 root root 148 Aug 17 2015 .profile
4.0K drwx------ 2 root root 4.0K Aug 3 05:32 .ssh
4.0K drwxr-xr-x 2 root root 4.0K Aug 3 04:42 test
4.0K -rw------- 1 root root 1.2K Aug 4 07:57 .viminfo
exit-status: 0
2.4 现在,我们测试一个无效命令abc
RunRemoteCommand.java
// Run a invalid command
channelExec.setCommand("abc");
输出
Terminal
bash: abc: command not found
exit-status: 127
错误将输出到System.err
。
下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-io
参考
Java . security . cert . certificate 异常:找不到与本地主机匹配的名称
问题
配置了 Tomcat 来支持 SSL 并部署了这个简单 hello world web 服务。并通过 SSL 连接使用以下客户端连接到已部署的 web 服务:
package com.mkyong.client;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import com.mkyong.ws.HelloWorld;
public class HelloWorldClient{
public static void main(String[] args) throws Exception {
URL url = new URL("https://localhost:8443/HelloWorld/hello?wsdl");
QName qname = new QName("http://ws.mkyong.com/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString());
}
}
命中“未找到与本地主机匹配的名称”异常:
Caused by: javax.net.ssl.SSLHandshakeException:
java.security.cert.CertificateException: No name matching localhost found
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1611)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
......
Caused by: java.security.cert.CertificateException: No name matching localhost found
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:210)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:77)
......
解决办法
这个问题和解决方案在这篇文章中有很好的解释,您可以为您的“ localhost 开发环境使用一个传输安全(SSL)解决方案。
要修复它,添加一个javax.net.ssl.HostnameVerifier()
方法来覆盖现有的主机名验证器,如下所示:
package com.mkyong.client;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import com.mkyong.ws.HelloWorld;
public class HelloWorldClient{
static {
//for localhost testing only
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
if (hostname.equals("localhost")) {
return true;
}
return false;
}
});
}
public static void main(String[] args) throws Exception {
URL url = new URL("https://localhost:8443/HelloWorld/hello?wsdl");
QName qname = new QName("http://ws.mkyong.com/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString());
}
}
输出
Hello World JAX-WS
它现在工作正常。
jax-ws web services (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190223080735/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
Java 序列化和反序列化示例
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/java-serialization-examples/
在 Java 中,Serialization
的意思是将 Java 对象转换成字节流;Deserialization
表示将序列化对象的字节流转换回原来的 Java 对象。
目录。
- 1。Hello World Java 序列化
- 2。Java . io . notserializableexception
- 3。什么是 serialVersionUID?
- 4。什么是瞬变?
- 5。将对象序列化到文件
- 6。为什么需要 Java 中的序列化?
- 7 .。不可信数据的反序列化
- 8。Java 9 反序列化过滤器
- 9。Java 17 特定于上下文的反序列化过滤器
- 下载源代码
- 参考文献
1。Hello World Java 序列化
在 Java 中,我们必须实现Serializable
接口来支持序列化和反序列化。
1.1 一个 Java 对象。
Person.java
package com.mkyong.io.object;
import java.io.Serializable;
import java.math.BigDecimal;
public class Person implements Serializable {
private String name;
private int age;
private BigDecimal salary;
// getters setters constructor
}
1.2 Java 对象Person.java
的完整 Java 序列化和反序列化示例。
HelloSerialization.java
package com.mkyong.io.object;
import java.io.*;
import java.math.BigDecimal;
public class HelloSerialization {
public static void main(String[] args) {
Person person = new Person("mkyong", 40, new BigDecimal(900));
byte[] bytes = convertObjectToBytes(person);
Person p = (Person) convertBytesToObject(bytes);
System.out.println(p);
}
// Convert object to byte[]
public static byte[] convertObjectToBytes(Object obj) {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
ois.writeObject(obj);
return boas.toByteArray();
} catch (IOException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
// Convert byte[] to object
public static Object convertBytesToObject(byte[] bytes) {
InputStream is = new ByteArrayInputStream(bytes);
try (ObjectInputStream ois = new ObjectInputStream(is)) {
return ois.readObject();
} catch (IOException | ClassNotFoundException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
}
输出
Terminal
Person{name='mkyong', age=40, salary=900}
2。Java . io . notserializableexception
如果我们序列化一个没有实现Serializable
接口的对象,Java 抛出java.io.NotSerializableException
。
Person.java
public class Person {
private String name;
private int age;
private BigDecimal salary;
// getters setters constructor
}
HelloSerialization.java
Person person = new Person("mkyong", 40, new BigDecimal(900));
byte[] bytes = convertObjectToBytes(person);
输出
Terminal
java.io.NotSerializableException: com.mkyong.io.object.Person
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1197)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at com.mkyong.io.object.HelloSerialization.convertObjectToBytes(HelloSerialization.java:22)
at com.mkyong.io.object.HelloSerialization.main(HelloSerialization.java:12)
Exception in thread "main" java.lang.RuntimeException
at com.mkyong.io.object.HelloSerialization.convertObjectToBytes(HelloSerialization.java:27)
at com.mkyong.io.object.HelloSerialization.main(HelloSerialization.java:12)
3。什么是 serialVersionUID?
如果serialVersionUID
丢失,JVM 将自动创建它。serialVersionUID
类似于版本号;简而言之,如果我们用1L
保存一个对象,我们需要提供相同的1L
来读取该对象,否则会遇到不兼容的错误。
Person.java
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
//...
}
例如,我们将一个带有serialVersionUID = 1L
的对象保存到一个文件名person.obj
中。稍后,我们从对象中添加或删除一些字段,并将serialVersionUID
更新为2L
。现在,读取person.obj
文件,尝试将其转换回修改后的对象;由于两者serialVersionUID
不同,我们会遇到以下不兼容的错误:
Terminal
Exception in thread "main" java.io.InvalidClassException: com.mkyong.io.object.Person;
local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1903)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1772)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
at com.mkyong.io.object.ObjectUtils.readObject(ObjectUtils.java:25)
at com.mkyong.io.object.ObjectUtils.main(ObjectUtils.java:38)
4。什么是瞬变?
在序列化过程中,JVM 忽略所有的transient
字段。如果我们需要在序列化过程中排除特定的对象字段,将它们标记为transient.
Person.java
public class Person implements Serializable {
private String name;
private int age;
//exclude this field
private transient BigDecimal salary;
// getters setters constructor
}
Person person = new Person("mkyong", 40, new BigDecimal(900));
byte[] bytes = convertObjectToBytes(person);
Person p = (Person) convertBytesToObject(bytes);
System.out.println(p);
输出
Terminal
Person{name='mkyong', age=40, salary=null}
5。将对象序列化到文件
此示例将 Java 对象序列化为文件,并将文件反序列化回原始对象。
HelloSerializationFile.java
package com.mkyong.io.object;
import java.io.*;
import java.math.BigDecimal;
public class HelloSerializationFile {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person("mkyong", 50, new BigDecimal(1000));
File file = new File("person.anything");
writeObjectToFile(person, file);
Person p = readObjectFromFile(file);
System.out.println(p);
}
// Serialization
// Save object into a file.
public static void writeObjectToFile(Person obj, File file) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(obj);
oos.flush();
}
}
// Deserialization
// Get object from a file.
public static Person readObjectFromFile(File file) throws IOException, ClassNotFoundException {
Person result = null;
try (FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis)) {
result = (Person) ois.readObject();
}
return result;
}
}
6。为什么需要 Java 中的序列化?
注
阅读这篇文章Brian Goetz——迈向更好的序列化。
Java 序列化的一些用例:
- 套接字、应用程序、客户端和服务器之间的数据交换格式。它喜欢 JSON 或 XML,但采用字节格式。
- 序列化是几项关键 Java 技术的基础,包括 Java 远程方法调用(Java RMI) 、公共对象请求代理架构(CORBA),以及分布式计算,如 Java 命名和目录接口(JNDI)、Java
管理扩展(JMX)和 Java 消息传递(JMS)。 - 对于作为轻量级持久性的游戏,我们可以在磁盘上序列化当前游戏的状态,并在以后恢复它。文件是字节格式的,我们不能轻易修改敏感游戏的数据,比如武器或者金钱。
- 保存一个对象图磁盘用于进一步分析,比如 UML 类图。
- 服务器集群和恢复会话。例如,如果其中一个服务器关闭或重启,服务器集群(服务器 A 和服务器 B)需要同步会话。当服务器 A 关闭时,它将对象保存为
HttpSession
的属性,并通过网络发送给服务器 B,服务器 B 可以从HttpSession
恢复对象。阅读这篇的帖子
对于数据交换,考虑人类可读的数据交换格式,如 JSON 、 XML 或 Google 协议缓冲区。
7。不可信数据的反序列化
但是,从不受信任的字节流进行反序列化是危险的,会导致以下 Java 漏洞:
- 一路向下——以嵌套容器为目标,比如列表中的列表。
- 串行拒绝服务(DOS)
- 河豚
- 远程代码执行(RCE)
延伸阅读
7.1 反序列化和堆栈溢出
反序列化下面的字节流,它抛出StackOverflowError
。
StackOverflowExample.java
package com.mkyong.io.object.attack;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class StackOverflowExample {
public static void main(String[] args) {
System.out.println(bomb().length);
deserialize(bomb()); // throws StackOverflow
System.out.println("Done");
}
static byte[] bomb() {
HashMap map = new HashMap();
List list = new ArrayList();
map.put(list, "");
list.add(list);
return serialize(map);
}
public static byte[] serialize(Object o) {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
try {
new ObjectOutputStream(ba).writeObject(o);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return ba.toByteArray();
}
public static Object deserialize(byte[] bytes) {
try {
return new ObjectInputStream(
new ByteArrayInputStream(bytes)).readObject();
} catch (IOException | ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
}
输出
Terminal
Exception in thread "main" java.lang.StackOverflowError
at java.base/java.util.ArrayList.hashCode(ArrayList.java:582)
at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:595)
//...
7.2 反序列化和拒绝服务攻击(DoS 攻击)
反序列化下面的字节流将保持进程运行,挂起,并慢慢降低系统速度,这是典型的 DoS 攻击。
DosExample.java
package com.mkyong.io.object.attack;
import java.io.*;
import java.util.HashSet;
import java.util.Set;
public class DosExample {
public static void main(String[] args) throws Exception {
System.out.println(bomb().length);
deserialize(bomb()); // Dos here
System.out.println("Done");
}
static byte[] bomb() {
Set<Object> root = new HashSet<>();
Set<Object> s1 = root;
Set<Object> s2 = new HashSet<>();
for (int i = 0; i < 100; i++) {
Set<Object> t1 = new HashSet<>();
Set<Object> t2 = new HashSet<>();
t1.add("test-" + i); // make it not equal to t2
s1.add(t1); // root also add set
s1.add(t2);
s2.add(t1);
s2.add(t2);
s1 = t1; // reference to t1, so that `root` can add new set from the last t1
s2 = t2;
}
return serialize(root);
}
public static byte[] serialize(Object o) {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
try {
new ObjectOutputStream(ba).writeObject(o);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return ba.toByteArray();
}
public static Object deserialize(byte[] bytes) {
try {
return new ObjectInputStream(
new ByteArrayInputStream(bytes)).readObject();
} catch (IOException | ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
}
输出
Terminal
// keep the process running, hanging there
// and slowly slowing down the system, a classic DoS attack.
8。Java 9 反序列化过滤器
Java 9, JEP 290 引入了反序列化过滤器ObjectInputFilter
来过滤传入的序列化数据。
8.1 stack overflow 的反序列化过滤器
用反序列化过滤器maxdepth=2
重构上面的StackOverflowExample.java
示例。如果我们重新运行程序,反序列化过程将停止并返回filter status: REJECTED
。
StackOverflowExample.java
package com.mkyong.io.object.attack;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class StackOverflowExample {
public static void main(String[] args) {
System.out.println(bomb().length);
//deserialize(bomb()); // throws StackOverflow
ObjectInputFilter filter =
ObjectInputFilter.Config.createFilter(
"maxdepth=2;java.base/*;!*");
// java.io.InvalidClassException: filter status: REJECTED
deserializeFilter(bomb(), filter);
System.out.println("Done");
}
static byte[] bomb() {
HashMap map = new HashMap();
List list = new ArrayList();
map.put(list, "");
list.add(list);
return serialize(map);
}
public static byte[] serialize(Object o) {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
try {
new ObjectOutputStream(ba).writeObject(o);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return ba.toByteArray();
}
public static Object deserializeFilter(byte[] bytes, ObjectInputFilter filter) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais)) {
// add filter before readObject
ois.setObjectInputFilter(filter);
return ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
}
输出
Terminal
Exception in thread "main" java.lang.IllegalArgumentException: java.io.InvalidClassException: filter status: REJECTED
at com.mkyong.io.object.attack.StackOverflowExample.deserializeFilter(StackOverflowExample.java:70)
at com.mkyong.io.object.attack.StackOverflowExample.main(StackOverflowExample.java:27)
Caused by: java.io.InvalidClassException: filter status: REJECTED
8.2 针对 DoS 攻击的反序列化过滤器
用maxdepth
反序列化过滤器重构上面的DosExample.java
示例,以阻止 DoS 攻击。
DosExample.java
package com.mkyong.io.object.attack;
import java.io.*;
import java.util.HashSet;
import java.util.Set;
public class DosExample {
public static void main(String[] args) throws Exception {
System.out.println(bomb().length);
//deserialize(bomb()); // Dos here
ObjectInputFilter filter =
ObjectInputFilter.Config.createFilter(
"maxdepth=10;java.base/*;!*");
deserializeFilter(bomb(), filter); // Dos here
System.out.println("Done");
}
static byte[] bomb() {
Set<Object> root = new HashSet<>();
Set<Object> s1 = root;
Set<Object> s2 = new HashSet<>();
for (int i = 0; i < 100; i++) {
Set<Object> t1 = new HashSet<>();
Set<Object> t2 = new HashSet<>();
t1.add("test-" + i); // make it not equal to t2
s1.add(t1); // root also add set
s1.add(t2);
s2.add(t1);
s2.add(t2);
s1 = t1; // reference to t1, so that `root` can add new set from the last t1
s2 = t2;
}
return serialize(root);
}
public static byte[] serialize(Object o) {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
try {
new ObjectOutputStream(ba).writeObject(o);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return ba.toByteArray();
}
public static Object deserializeFilter(byte[] bytes, ObjectInputFilter filter) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais)) {
// add filter before readObject
ois.setObjectInputFilter(filter);
return ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
}
如果我们重新运行程序,反序列化过程将停止并返回filter status: REJECTED
。
Terminal
Exception in thread "main" java.lang.IllegalArgumentException:
java.io.InvalidClassException: filter status: REJECTED
at com.mkyong.io.object.attack.DosExample.deserializeFilter(DosExample.java:74)
at com.mkyong.io.object.attack.DosExample.main(DosExample.java:19)
注
阅读关于序列化过滤的官方指南。
9。Java 17 特定于上下文的反序列化过滤器
Java 17 JEP 415 引入了一个过滤器工厂,它允许动态或上下文相关地选择不同的反序列化过滤器,参考 Java 17 过滤器工厂示例。
下载源代码
$ git 克隆https://github.com/mkyong/core-java.git
$ CD Java-io/com/mkyong/io/object
参考文献
- Java 版本历史
- 序列化过滤
- OWASP–不可信数据的反序列化
- Brian Goetz——迈向更好的序列化
- 邪恶泡菜:基于对象图工程的 DoS 攻击
- Java 对象序列化规范
- 可序列化的 JavaDoc
- ObjectInputStream JavaDoc
- ObjectOutputStreamJavaDoc
- Java–什么是 serialVersionUID
- JEP 290:过滤输入的串行化数据
- WebLogic、WebSphere、JBoss、Jenkins、OpenNMS 和您的应用程序有什么共同点?这个漏洞。
- Java 序列化漏洞
java.sql.SQLException:不允许操作:序号绑定和命名绑定不能组合!
顺序绑定或索引绑定:
String name = stat.getString(2);
BigDecimal salary = stat.getBigDecimal(3);
Timestamp createdDate = stat.getTimestamp(4);
命名绑定:
String name = stat.getString("NAME");
BigDecimal salary = stat.getBigDecimal("SALARY");
Timestamp createdDate = stat.getTimestamp("CREATED_DATE");
如果我们像这样混合两者:
String name = stat.getString(2);
BigDecimal salary = stat.getBigDecimal("SALARY");
Timestamp createdDate = stat.getTimestamp(4);
错误:
java.sql.SQLException: operation not allowed:
Ordinal binding and Named binding cannot be combined!
JDBC 可赎回声明
调用进出存储过程的CallableStatement
示例:
// IN with ordinal binding
callableStatement.setInt(1, 999);
// OUT with name binding
String name = callableStatement.getString("NAME");
BigDecimal salary = callableStatement.getBigDecimal("SALARY");
Timestamp createdDate = callableStatement.getTimestamp("CREATED_DATE");
输出
java.sql.SQLException: operation not allowed:
Ordinal binding and Named binding cannot be combined!
要修复此问题,请将 all 更新为序号绑定或名称绑定:
callableStatement.setInt(1, 999);
String name = callableStatement.getString(2);
BigDecimal salary = callableStatement.getBigDecimal(3);
Timestamp createdDate = callableStatement.getTimestamp(4);
参考
java.sql.SQLException:无法识别服务器时区值“xx time”
使用最新的mysql-connector-java:8.0.16
与 MySQL 服务器建立 JDBC 连接,并遇到以下 SQLException:
用测试
- MySQL 5.7
- Java 8
- JDBC 驱动程序,MySQL-连接器-java 8.0.16
try (Connection conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/test", "root", "password")) {
//...
} catch (Exception e) {
e.printStackTrace();
}
输出
java.sql.SQLException: The server time zone value 'Malay Peninsula Standard Time' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:73)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:76)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:455)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:677)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:228)
at com.mkyong.jdbc.JDBCExample.main(JDBCExample.java:23)
Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Malay Peninsula Standard Time' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:85)
at com.mysql.cj.util.TimeUtil.getCanonicalTimezone(TimeUtil.java:132)
at com.mysql.cj.protocol.a.NativeProtocol.configureTimezone(NativeProtocol.java:2243)
at com.mysql.cj.protocol.a.NativeProtocol.initServerSession(NativeProtocol.java:2267)
at com.mysql.cj.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:1319)
at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:966)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825)
... 6 more
1.解决办法
1.1 找到 MySQL 配置文件,例如my.ini
或my.cnf
。在文件末尾添加默认时区:
my.ini
# Indicates the InnoDB Cluster Port.
# innodbclusterport=3306
# Load mysql plugins at start."plugin_x ; plugin_y".
# plugin_load
# MySQL server's plugin configuration.
# loose_mysqlx_port=33060
# Set default time zone
default-time-zone = '+08:00'
1.2 重启 MySQL 服务。
附注:阅读这个 MySQL 选项文件,找出文件(my.ini
或my.cnf
)的位置。
2.解决办法
或者,直接在 JDBC 连接字符串中传递一个serverTimezone
属性。
try (Connection conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC", "root", "password")) {
//...
} catch (Exception e) {
e.printStackTrace();
}
参考
- 使用 JDBC 驱动程序连接 MySQL】
- MySQL 选项文件
- 如何设置 MySQL 的时区?
Java–流已经被操作或关闭
在 Java 8 中,流不能被重用,一旦被消耗或使用,该流将被关闭。
1.示例–流已关闭!
回顾下面的例子,它会抛出一个IllegalStateException
,说“stream closed”。
TestJava8.java
package com.mkyong.java8;
import java.util.Arrays;
import java.util.stream.Stream;
public class TestJava8 {
public static void main(String[] args) {
String[] array = {"a", "b", "c", "d", "e"};
Stream<String> stream = Arrays.stream(array);
// loop a stream
stream.forEach(x -> System.out.println(x));
// reuse it to filter again! throws IllegalStateException
long count = stream.filter(x -> "b".equals(x)).count();
System.out.println(count);
}
}
输出
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at com.hostingcompass.whois.range.run.TestJava8.main(TestJava8.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
2.示例–重用流
不管出于什么原因,你真的想重用一个流,尝试下面的Supplier
解决方案:
TestJava8.java
package com.mkyong.java8;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TestJava8 {
public static void main(String[] args) {
String[] array = {"a", "b", "c", "d", "e"};
Supplier<Stream<String>> streamSupplier = () -> Stream.of(array);
//get new stream
streamSupplier.get().forEach(x -> System.out.println(x));
//get another new stream
long count = streamSupplier.get().filter(x -> "b".equals(x)).count();
System.out.println(count);
}
}
输出
a
b
c
d
e
1
每个get()
将返回一个新的流。
Why?
May I know why you need to reuse a stream, for testing?
参考
Java XML 教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/java-xml-tutorials/
在这一系列 Java XML 教程中,我们将展示如何使用 DOM、SAX、StAX 和 JDOM 等 XML 解析器来读写 XML 文档;另外,JAXB 将 XML 转换成对象或从对象转换成 XML。
一般来说,有两种处理 XML 文档的编程模型:DOM 和 SAX(流)。
目录
- 1。文档对象模型
- 2。XML 的简单应用编程接口(SAX)
- 3。XML 流应用编程接口(StAX)
- 4。第三方 XML 解析器(JDOM)
- 5。用于 XML 绑定的 Java 架构(JAXB)
- 6。Java XML 常见问题解答
- 7。下载源代码
- 8。参考文献
DOM、SAX 和 StAX 都是 Java APIs 的一部分。
1。文档对象模型
文档对象模型(DOM) 使用节点将整个 XML 文档表示为一个树形结构,并将它们存储在内存中。
DOM 有利于操纵小的 XML 文件,比如读、写和修改 XML 结构;DOM 不用于解析或操作大型 XML 文件,因为在内存中构建整个 XML 结构会消耗大量内存。
DOM 解析器示例
2。XML 的简单应用编程接口(SAX)
Simple API for XML (SAX)是一个流模型、事件驱动、推送解析 API,用于读取 XML 文档(需要另一个 API 来编写)。SAX 从头到尾读取 XML 文件,当遇到一个元素时调用一个方法,或者当找到特定的文本或属性时调用另一个方法。
SAX 快速高效,比 DOM 需要更少的内存,因为 SAX 不像 DOM 那样创建 XML 数据的内部表示(树结构)。
SAX 解析器示例
3。XML 流应用编程接口(StAX)
XML 流 API(StAX)是一个流模型、事件驱动、用于读写 XML 文档的拉解析 API。StAX 提供了比 SAX 更简单的编程模型和比 DOM 更高效的内存管理。
StAX 解析器示例
4。第三方 XML 解析器(JDOM)
DOM、SAX 和 StAX 是 Java APIs 的一部分。然而,原料药可能不适合每个人的口味。或者,我们可以使用第三方 XML 解析器:
JDOM 解析器示例
5。用于 XML 绑定的 Java 架构(JAXB)
Jakarta XML 绑定(JAXB;以前的 Java Architecture for XML Binding)是一个 XML 绑定框架,用于在 Java 类和 XML 之间进行转换。
JAXB 示例
6。Java XML 常见问题解答
一些常见问题。
7。下载源代码
$ git 克隆https://github.com/mkyong/core-java
$ cd java-xml
$ CD src/main/Java/com/mkyong/XML/
8。参考文献
- 维基百科-XML
- 维基百科–用于 XML 处理的 Java API
- 维基百科–文档对象模型
- 维基百科–XML 的简单 API
- Oracle–用于 XML 处理的 Java API(JAXP)
- Oracle–文档对象模型
- Oracle–XML 简单应用编程接口(SAX)
- Oracle Streaming API for XML(StAX)
- StAX 简介
javax . naming . namenotfoundexception:名称 jdbc 未在此上下文中绑定
问题
JSF 2.0 web 应用程序,一个托管 bean 使用@Resource
将数据源jdbc/mkyongdb
注入到一个 ds 属性中。
@ManagedBean(name="customer")
@SessionScoped
public class CustomerBean implements Serializable{
//resource injection
@Resource(name="jdbc/mkyongdb")
private DataSource ds;
当部署到 Tomcat 6 时,它会遇到以下 MySQL 数据源配置的错误消息。
com.sun.faces.mgbean.ManagedBeanCreationException:
An error occurred performing resource injection on managed bean customer
at com.sun.faces.mgbean.BeanBuilder.injectResources(BeanBuilder.java:207)
Caused by: com.sun.faces.spi.InjectionProviderException:
javax.naming.NameNotFoundException: Name jdbc is not bound in this Context
at com.sun.faces.vendor.Tomcat6InjectionProvider.inject(Tomcat6InjectionProvider.java:84)
at com.sun.faces.mgbean.BeanBuilder.injectResources(BeanBuilder.java:201)
... 53 more
Caused by: javax.naming.NameNotFoundException: Name jdbc is not bound in this Context
at org.apache.naming.NamingContext.lookup(NamingContext.java:770)
at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
... 54 more
解决办法
“JDBC/mkyondb”数据源在 Tomcat 中配置不正确,请参阅本指南了解详细信息–如何在 Tomcat 6 中配置 MySQL 数据源
jdbc jsf2 (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById®;if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!‘undefined’){i[c]++} else{i[c]=1;} })(window, document, ‘InContent’, ‘script’, ‘mediaType’, ‘carambola_proxy’,‘Cbola_IC’,‘localStorage’,‘set’,‘get’,‘Item’,‘cbolaDt’,‘//web.archive.org/web/20190224205626/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0’)
javax.swing.tree.TreeNode 是一个受限类
问题
在以下环境中,在 Google App Engine 上开发 Struts2。
- struts 2.3.1.2
- freemaker 2.3.18
- JDK 1.6
- Eclipse 3.7+Eclipse 的 Google 插件
- 谷歌应用引擎 Java SDK 1.6.3.1
GAE 抱怨说javax.swing.tree.TreeNode
是本地开发中的受限类,如果部署在真实的 GAE 生产环境中,错误消息就消失了。
Caused by:
java.lang.NoClassDefFoundError: javax.swing.tree.TreeNode is a restricted class.
Please see the Google App Engine developer's guide for more details.
at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51)
at freemarker.core.TextBlock.isIgnorable(TextBlock.java:375)
at freemarker.core.TextBlock.heedsTrailingWhitespace(TextBlock.java:337)
解决办法
不知道为什么它能在 GAE 生产环境中工作,对于 GAE 本地环境,你可以重载TextBlock
类,编译下面的代码并将其移动到 WEB-INF/classes ,这样它将重载原来的TextBlock
类。
文件:TextBlock.java
/*
* Copyright (c) 2003 The Visigoth Software Society. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1\. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2\. Redistributions in binary form must reproduce the above
copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3\. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Visigoth Software Society (http://www.visigoths.org/)."
* Alternately, this acknowledgement may appear in the software
itself,
* if and wherever such third-party acknowledgements normally
appear.
*
* 4\. Neither the name "FreeMarker", "Visigoth", nor any of the names
of the
* project contributors may be used to endorse or promote products
derived
* from this software without prior written permission. For written
* permission, please contact visigo...@visigoths.org.
*
* 5\. Products derived from this software may not be called
"FreeMarker" or "Visigoth"
* nor may "FreeMarker" or "Visigoth" appear in their names
* without prior written permission of the Visigoth Software
Society.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Visigoth Software Society. For more
* information on the Visigoth Software Society, please see
* http://www.visigoths.org/
*/
package freemarker.core;
import java.io.IOException;
/**
* A TemplateElement representing a block of plain text.
*
* @version $Id: TextBlock.java,v 1.17 2004/01/06 17:06:42 szegedia Exp $
*/
public final class TextBlock extends TemplateElement {
private static final char[] EMPTY_CHAR_ARRAY = new char[0];
static final TextBlock EMPTY_BLOCK = new TextBlock(EMPTY_CHAR_ARRAY, false);
// We're using char[] instead of String for storing the text block because
// Writer.write(String) involves copying the String contents to a char[]
// using String.getChars(), and then calling Writer.write(char[]). By
// using Writer.write(char[]) directly, we avoid array copying on each
// write.
private char[] text;
private final boolean unparsed;
public TextBlock(String text) {
this(text, false);
}
public TextBlock(String text, boolean unparsed) {
this(text.toCharArray(), unparsed);
}
private TextBlock(char[] text, boolean unparsed) {
this.text = text;
this.unparsed = unparsed;
}
/**
* Simply outputs the text.
*/
public void accept(Environment env) throws IOException {
env.getOut().write(text);
}
public String getCanonicalForm() {
String text = new String(this.text);
if (unparsed) {
return "<#noparse>" + text + "</#noparse>";
}
return text;
}
public String getDescription() {
String s = new String(text).trim();
if (s.length() == 0) {
return "whitespace";
}
if (s.length() > 20) {
s = s.substring(0, 20) + "...";
s = s.replace('\n', ' ');
s = s.replace('\r', ' ');
}
return "text block (" + s + ")";
}
TemplateElement postParseCleanup(boolean stripWhitespace) {
if (text.length == 0)
return this;
int openingCharsToStrip = 0, trailingCharsToStrip = 0;
boolean deliberateLeftTrim = deliberateLeftTrim();
boolean deliberateRightTrim = deliberateRightTrim();
if (!stripWhitespace || text.length == 0) {
return this;
}
if (parent.parent == null && previousSibling() == null)
return this;
if (!deliberateLeftTrim) {
trailingCharsToStrip = trailingCharsToStrip();
}
if (!deliberateRightTrim) {
openingCharsToStrip = openingCharsToStrip();
}
if (openingCharsToStrip == 0 && trailingCharsToStrip == 0) {
return this;
}
this.text = substring(text, openingCharsToStrip, text.length
- trailingCharsToStrip);
if (openingCharsToStrip > 0) {
this.beginLine++;
this.beginColumn = 1;
}
if (trailingCharsToStrip > 0) {
this.endColumn = 0;
}
return this;
}
/**
* Scans forward the nodes on the same line to see whether there is a
* deliberate left trim in effect. Returns true if the left trim was
* present.
*/
private boolean deliberateLeftTrim() {
boolean result = false;
for (TemplateElement elem = this.nextTerminalNode(); elem != null
&& elem.beginLine == this.endLine; elem = elem
.nextTerminalNode()) {
if (elem instanceof TrimInstruction) {
TrimInstruction ti = (TrimInstruction) elem;
if (!ti.left && !ti.right) {
result = true;
}
if (ti.left) {
result = true;
int lastNewLineIndex = lastNewLineIndex();
if (lastNewLineIndex >= 0 || beginColumn == 1) {
char[] firstPart = substring(text, 0,
lastNewLineIndex + 1);
char[] lastLine = substring(text, 1 + lastNewLineIndex);
if (trim(lastLine).length == 0) {
this.text = firstPart;
this.endColumn = 0;
} else {
int i = 0;
while (Character.isWhitespace(lastLine[i])) {
i++;
}
char[] printablePart = substring(lastLine, i);
this.text = concat(firstPart, printablePart);
}
}
}
}
}
if (result) {
}
return result;
}
/**
* Checks for the presence of a t or rt directive on the same line. Returns
* true if the right trim directive was present.
*/
private boolean deliberateRightTrim() {
boolean result = false;
for (TemplateElement elem = this.prevTerminalNode(); elem != null
&& elem.endLine == this.beginLine; elem = elem
.prevTerminalNode()) {
if (elem instanceof TrimInstruction) {
TrimInstruction ti = (TrimInstruction) elem;
if (!ti.left && !ti.right) {
result = true;
}
if (ti.right) {
result = true;
int firstLineIndex = firstNewLineIndex() + 1;
if (firstLineIndex == 0) {
return false;
}
if (text.length > firstLineIndex
&& text[firstLineIndex - 1] == '\r'
&& text[firstLineIndex] == '\n') {
firstLineIndex++;
}
char[] trailingPart = substring(text, firstLineIndex);
char[] openingPart = substring(text, 0, firstLineIndex);
if (trim(openingPart).length == 0) {
this.text = trailingPart;
this.beginLine++;
this.beginColumn = 1;
} else {
int lastNonWS = openingPart.length - 1;
while (Character.isWhitespace(text[lastNonWS])) {
lastNonWS--;
}
char[] printablePart = substring(text, 0, lastNonWS + 1);
if (trim(trailingPart).length == 0) {
// THIS BLOCK IS HEINOUS! THERE MUST BE A BETTER
// WAY! REVISIT (JR)
boolean trimTrailingPart = true;
for (TemplateElement te = this.nextTerminalNode(); te != null
&& te.beginLine == this.endLine; te = te
.nextTerminalNode()) {
if (te.heedsOpeningWhitespace()) {
trimTrailingPart = false;
}
if (te instanceof TrimInstruction
&& ((TrimInstruction) te).left) {
trimTrailingPart = true;
break;
}
}
if (trimTrailingPart)
trailingPart = EMPTY_CHAR_ARRAY;
}
this.text = concat(printablePart, trailingPart);
}
}
}
}
return result;
}
/*
* private String leftTrim(String s) { int i =0; while (i<s.length()) { if
* (!Character.isWhitespace(s.charAt(i))) break; ++i; } return
* s.substring(i); }
*/
private int firstNewLineIndex() {
String content = new String(text);
int newlineIndex1 = content.indexOf('\n');
int newlineIndex2 = content.indexOf('\r');
int result = newlineIndex1 >= 0 ? newlineIndex1 : newlineIndex2;
if (newlineIndex1 >= 0 && newlineIndex2 >= 0) {
result = Math.min(newlineIndex1, newlineIndex2);
}
return result;
}
private int lastNewLineIndex() {
String content = new String(text);
return Math.max(content.lastIndexOf('\r'), content.lastIndexOf('\n'));
}
/**
* figures out how many opening whitespace characters to strip in the
* post-parse cleanup phase.
*/
private int openingCharsToStrip() {
int newlineIndex = firstNewLineIndex();
if (newlineIndex == -1 && beginColumn != 1) {
return 0;
}
++newlineIndex;
if (text.length > newlineIndex) {
if (newlineIndex > 0 && text[newlineIndex - 1] == '\r'
&& text[newlineIndex] == '\n') {
++newlineIndex;
}
}
if (new String(text).substring(0, newlineIndex).trim().length() > 0) {
return 0;
}
// We look at the preceding elements on the line to see if we should
// strip the opening newline and any whitespace preceding it.
for (TemplateElement elem = this.prevTerminalNode(); elem != null
&& elem.endLine == this.beginLine; elem = elem
.prevTerminalNode()) {
if (elem.heedsOpeningWhitespace()) {
return 0;
}
}
return newlineIndex;
}
/**
* figures out how many trailing whitespace characters to strip in the
* post-parse cleanup phase.
*/
private int trailingCharsToStrip() {
String content = new String(text);
int lastNewlineIndex = lastNewLineIndex();
if (lastNewlineIndex == -1 && beginColumn != 1) {
return 0;
}
String substring = content.substring(lastNewlineIndex +1);
if (substring.trim().length() >0) {
return 0;
}
// We look at the elements afterward on the same line to see if we should
// strip any whitespace after the last newline
for (TemplateElement elem = this.nextTerminalNode();
elem != null && elem.beginLine == this.endLine;
elem = elem.nextTerminalNode())
{
if (elem.heedsTrailingWhitespace())
{
return 0;
}
}
return substring.length();
}
boolean heedsTrailingWhitespace() {
if (isIgnorable()) {
return false;
}
for (int i = 0; i < text.length; i++) {
char c = text[i];
if (c == '\n' || c == '\r') {
return false;
}
if (!Character.isWhitespace(c)) {
return true;
}
}
return true;
}
boolean heedsOpeningWhitespace() {
if (isIgnorable()) {
return false;
}
for (int i = text.length - 1; i >= 0; i--) {
char c = text[i];
if (c == '\n' || c == '\r') {
return false;
}
if (!Character.isWhitespace(c)) {
return true;
}
}
return true;
}
boolean isIgnorable() {
if (text == null || text.length == 0) {
return true;
}
if (!isWhitespace()) {
return false;
}
// do the trick
boolean atTopLevel = true;
TemplateElement prevSibling = previousSibling();
TemplateElement nextSibling = nextSibling();
return ((prevSibling == null && atTopLevel) || nonOutputtingType(prevSibling))
&& ((nextSibling == null && atTopLevel) || nonOutputtingType(nextSibling));
}
private boolean nonOutputtingType(TemplateElement element) {
return (element instanceof Macro || element instanceof Assignment
|| element instanceof AssignmentInstruction
|| element instanceof PropertySetting
|| element instanceof LibraryLoad || element instanceof Comment);
}
private static char[] substring(char[] c, int from, int to) {
char[] c2 = new char[to - from];
System.arraycopy(c, from, c2, 0, c2.length);
return c2;
}
private static char[] substring(char[] c, int from) {
return substring(c, from, c.length);
}
private static char[] trim(char[] c) {
if (c.length == 0) {
return c;
}
return new String(c).trim().toCharArray();
}
private static char[] concat(char[] c1, char[] c2) {
char[] c = new char[c1.length + c2.length];
System.arraycopy(c1, 0, c, 0, c1.length);
System.arraycopy(c2, 0, c, c1.length, c2.length);
return c;
}
boolean isWhitespace() {
return text == null || trim(text).length == 0;
}
}
参考
JAX-遥感教程
原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/jax-rs-tutorials/
Java API for RESTful Web Services(JAX-RS),是一套为开发者提供 REST 服务的 API。JAX-RS 是 Java EE6 的一部分,使开发者开发 REST web 应用变得容易。
在这一系列的 JAX-RS 教程中,我们同时使用了 Jersey 和 RESTEasy ,流行的 JAX-RS 实现。
快乐学习 JAX🙂
快速启动
一些快速使用 JAX 遥感器的例子。
- Jersey hello world 示例
Jersey 框架创建一个简单的 REST 风格的 web 应用程序。 - RESTEasy hello world 示例
RESTEasy 框架创建一个简单的 REST 风格的 web 应用程序。
基本示例
开发 REST 服务的基本注释和函数。
- JAX-RS @Path URI 匹配示例
JAX-RS URI 匹配示例。 - JAX-RS @PathParam 示例
将@Path 中定义的 URI 参数注入 Java 方法的简单方法。 - JAX-RS @QueryParam 示例
示例获取 URI 路径中的查询参数,以及如何定义可选参数。 - JAX-RS @MatrixParam 示例
获取 URI 路径中矩阵参数的示例。 - JAX-RS @FormParam 示例
获取 HTML post 表单参数值的示例。 - 展示了如何使用@HeaderParam 和@Context 来获取 HTTP 头。
- 从 JAX 下载文本文件-RS
示例输出一个文本文件供用户下载。 - 从 JAX 下载图像文件-RS
示例输出图像文件供用户下载。 - 从 JAX 下载 pdf 文件-RS
示例输出 pdf 文件供用户下载。 - 从 JAX 下载 excel 文件-RS
示例输出 excel 文件供用户下载。
文件上传示例
如何处理 JAX 遥感中的多部分数据?
- 文件上传的例子在泽
文件上传在泽很容易。 - RESTEasy 中的文件上传示例
RESTEasy 中处理文件上传的两种方式。
使用 XML
JAX 遥感系统中的 XML 支持。
- XML 示例用 Jersey + JAXB
Jersey + JAXB 将对象映射到 XML 和从 XML 映射。 - XML 示例用 RESTEasy+JAXB
RESTEasy+JAXB 将对象映射到 XML 和从 XML 映射。
使用 JSON
JAX 的 JSON 支持。
- JSON 示例用 Jersey+Jackson
Jersey+Jackson 将对象映射到 JSON 和从 JSON 映射。 - JSON 示例用 rest easy+Jackson
rest easy+Jackson 将对象映射到 JSON 和从 JSON 映射。 - JSON 示例用 rest easy+JAXB+spoken
rest easy+JAXB+spoken 将对象映射到 JSON 和从 JSON 映射。
RESTful Java 客户端
创建一个 RESTful Java 客户端来执行“GET”和“POST”请求,以操作 json 数据。
- 使用 java.net.URL 的 RESTful Java 客户端
- 带 Apache HttpClient 的 RESTful Java 客户端
- 带有 RESTEasy 客户端的 RESTful Java 客户端
- RESTful Java 客户端和 Jersey 客户端
JAX-遥感+春天
将 JAX 遥感系统与 Spring 框架集成。
- 球衣+弹簧整合实例
将球衣与弹簧框架整合。 - RESTEasy + Spring 集成实例
将 RESTEasy 与 Spring 框架集成。
常见错误消息
JAX 遥感器开发中的一些常见错误信息。
- RESTEasy 无法扫描 WEB-INF 中的 JAX-RS 注释,ZLIB 输入流意外结束
- ClassNotFoundException:org . JBoss . rest easy . plugins . providers . multipart . multipart input
- rest easy–找不到类型为 multipart/form-data 的消息正文读取器
- rest easy–找不到类型为 xx、媒体类型为 application/xml 的响应对象的 MessageBodyWriter】
- 将消息体注入到公共 org . codehaus . Jackson . jaxrs . jacksonjsonprovider 的单例中是非法的
- Jersey:resource config 实例不包含任何根资源类
- ClassNotFoundException:com . sun . jersey . SPI . container . servlet . servlet container
参考
JAX-WS 与 MTOM 的关系
一个完整的基于 JAX-WS SOAP 的示例,展示了如何使用消息传输优化机制(MTOM) 和XML-二进制优化打包(XOP) 技术在服务器和客户端之间发送二进制附件(图像)。
Note
There are ton of articles about what’s MTOM and the benefits of using it (see reference sites below), but it’s lack of complete example to use MTOM in JAX-WS, hope this example can fill in some missing pieces in the whole picture.
在服务器上启用 MTOM
让服务器通过 MTOM 发送附件非常容易,只需用javax.xml.ws.soap.MTOM
注释 web 服务实现类。
1.web 服务端点
这里有一个 RPC 风格的 web 服务,发布了两个方法,downloadImage(String name)
和uploadImage(Image data)
,让用户上传或下载图像文件。
文件:ImageServer.java
package com.mkyong.ws;
import java.awt.Image;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
//Service Endpoint Interface
@WebService
@SOAPBinding(style = Style.RPC)
public interface ImageServer{
//download a image from server
@WebMethod Image downloadImage(String name);
//update image to server
@WebMethod String uploadImage(Image data);
}
文件:ImageServerImpl.java
package com.mkyong.ws;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;
//Service Implementation Bean
@MTOM
@WebService(endpointInterface = "com.mkyong.ws.ImageServer")
public class ImageServerImpl implements ImageServer{
@Override
public Image downloadImage(String name) {
try {
File image = new File("c:\\images\\" + name);
return ImageIO.read(image);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public String uploadImage(Image data) {
if(data!=null){
//store somewhere
return "Upload Successful";
}
throw new WebServiceException("Upload Failed!");
}
}
文件:ImagePublisher.java
package com.mkyong.endpoint;
import javax.xml.ws.Endpoint;
import com.mkyong.ws.ImageServerImpl;
//Endpoint publisher
public class ImagePublisher{
public static void main(String[] args) {
Endpoint.publish("http://localhost:9999/ws/image", new ImageServerImpl());
System.out.println("Server is published!");
}
}
2.web 服务客户端
这里有一个 web 服务客户端,用于访问位于 URL "http://localhost:9999/ws/image的已发布的 web 服务。
文件:ImageClient.java
package com.mkyong.client;
import java.awt.Image;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.soap.MTOMFeature;
import javax.xml.ws.soap.SOAPBinding;
import com.mkyong.ws.ImageServer;
public class ImageClient{
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:9999/ws/image?wsdl");
QName qname = new QName("http://ws.mkyong.com/", "ImageServerImplService");
Service service = Service.create(url, qname);
ImageServer imageServer = service.getPort(ImageServer.class);
/************ test download ***************/
Image image = imageServer.downloadImage("rss.png");
//display it in frame
JFrame frame = new JFrame();
frame.setSize(300, 300);
JLabel label = new JLabel(new ImageIcon(image));
frame.add(label);
frame.setVisible(true);
System.out.println("imageServer.downloadImage() : Download Successful!");
}
}
3.HTTP 和 SOAP 流量
这是客户端生成的 HTTP 和 SOAP 流量,由流量监控工具捕获。
为了节省空间,省略了第一个 wsdl 请求。
客户端发送请求:
POST /ws/image HTTP/1.1
SOAPAction: ""
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Content-Type: text/xml; charset=utf-8
User-Agent: Java/1.6.0_13
Host: localhost:9999
Connection: keep-alive
Content-Length: 209
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:downloadImage xmlns:ns2="http://ws.mkyong.com/">
<arg0>rss.png</arg0>
</ns2:downloadImage>
</S:Body>
</S:Envelope>
服务器发送响应:
HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: multipart/related;
start="<rootpart*c73c9ce8-6e02-40ce-9f68-064e18843428@example.jaxws.sun.com>";
type="application/xop+xml";
boundary="uuid:c73c9ce8-6e02-40ce-9f68-064e18843428";
start-info="text/xml"
--uuid:c73c9ce8-6e02-40ce-9f68-064e18843428
Content-Id: <rootpart*c73c9ce8-6e02-40ce-9f68-064e18843428@example.jaxws.sun.com>
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
Content-Transfer-Encoding: binary
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:downloadImageResponse xmlns:ns2="http://ws.mkyong.com/">
<return>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="cid:012eb00e-9460-407c-b622-1be987fdb2cf@example.jaxws.sun.com">
</xop:Include>
</return>
</ns2:downloadImageResponse>
</S:Body>
</S:Envelope>
--uuid:c73c9ce8-6e02-40ce-9f68-064e18843428
Content-Id: <012eb00e-9460-407c-b622-1be987fdb2cf@example.jaxws.sun.com>
Content-Type: image/png
Content-Transfer-Encoding: binary
Binary data here.............
在客户端启用 MTOM
允许客户端通过 MTOM 向服务器发送附件需要一些额外的工作,请参见以下示例:
//codes enable MTOM in client
BindingProvider bp = (BindingProvider) imageServer;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
1.web 服务客户端
下面是一个 webservice 客户端,它通过 MTOM 将一个图像文件发送到上面发布的端点(http://localhost:8888/ws/image)。
文件:ImageClient.java
package com.mkyong.client;
import java.awt.Image;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.soap.MTOMFeature;
import javax.xml.ws.soap.SOAPBinding;
import com.mkyong.ws.ImageServer;
public class ImageClient{
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:8888/ws/image?wsdl");
QName qname = new QName("http://ws.mkyong.com/", "ImageServerImplService");
Service service = Service.create(url, qname);
ImageServer imageServer = service.getPort(ImageServer.class);
/************ test upload ****************/
Image imgUpload = ImageIO.read(new File("c:\\images\\rss.png"));
//enable MTOM in client
BindingProvider bp = (BindingProvider) imageServer;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
String status = imageServer.uploadImage(imgUpload);
System.out.println("imageServer.uploadImage() : " + status);
}
}
2.HTTP 和 SOAP 流量
这是客户端生成的 HTTP 和 SOAP 流量,由流量监控工具捕获。
为了节省空间,省略了第一个 wsdl 请求。
客户端发送请求:
POST /ws/image HTTP/1.1
Content-type: multipart/related;
start="<rootpart*751f2e5d-47f8-47d8-baf0-f793c29bd931@example.jaxws.sun.com>";
type="application/xop+xml";
boundary="uuid:751f2e5d-47f8-47d8-baf0-f793c29bd931";
start-info="text/xml"
Soapaction: ""
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: JAX-WS RI 2.1.6 in JDK 6
Host: localhost:9999
Connection: keep-alive
Content-Length: 6016
--uuid:751f2e5d-47f8-47d8-baf0-f793c29bd931
Content-Id: <rootpart*751f2e5d-47f8-47d8-baf0-f793c29bd931@example.jaxws.sun.com>
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
Content-Transfer-Encoding: binary
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:uploadImage xmlns:ns2="http://ws.mkyong.com/">
<arg0>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="cid:2806f201-e15e-4ee0-8347-b7b4dffad5cb@example.jaxws.sun.com">
</xop:Include>
</arg0>
</ns2:uploadImage>
</S:Body>
</S:Envelope>
--uuid:751f2e5d-47f8-47d8-baf0-f793c29bd931
Content-Id: <2806f201-e15e-4ee0-8347-b7b4dffad5cb@example.jaxws.sun.com>
Content-Type: image/png
Content-Transfer-Encoding: binary
Binary data here.............
服务器发送响应:
HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: multipart/related;
start="<rootpart*188a5835-198b-4c28-9b36-bf030578f2bd@example.jaxws.sun.com>";
type="application/xop+xml";
boundary="uuid:188a5835-198b-4c28-9b36-bf030578f2bd";
start-info="text/xml"
--uuid:188a5835-198b-4c28-9b36-bf030578f2bd
Content-Id: <rootpart*188a5835-198b-4c28-9b36-bf030578f2bd@example.jaxws.sun.com>
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
Content-Transfer-Encoding: binary
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:uploadImageResponse xmlns:ns2="http://ws.mkyong.com/">
<return>Upload Successful</return>
</ns2:uploadImageResponse>
</S:Body>
</S:Envelope>
--uuid:188a5835-198b-4c28-9b36-bf030578f2bd--
完整的 WSDL 文档
有兴趣研究 WSDL 文件的,可以通过 URL:http://localhost:9999/ws/image 获取 wsdl 文件?wsdl
ImageServer WSDL 文件示例
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://ws.mkyong.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://ws.mkyong.com/"
name="ImageServerImplService">
<types></types>
<message name="downloadImage">
<part name="arg0" type="xsd:string"></part>
</message>
<message name="downloadImageResponse">
<part name="return" type="xsd:base64Binary"></part>
</message>
<message name="uploadImage">
<part name="arg0" type="xsd:base64Binary"></part>
</message>
<message name="uploadImageResponse">
<part name="return" type="xsd:string"></part>
</message>
<portType name="ImageServer">
<operation name="downloadImage">
<input message="tns:downloadImage"></input>
<output message="tns:downloadImageResponse"></output>
</operation>
<operation name="uploadImage">
<input message="tns:uploadImage"></input>
<output message="tns:uploadImageResponse"></output>
</operation>
</portType>
<binding name="ImageServerImplPortBinding" type="tns:ImageServer">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc">
</soap:binding>
<operation name="downloadImage">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal" namespace="http://ws.mkyong.com/"></soap:body>
</input>
<output>
<soap:body use="literal" namespace="http://ws.mkyong.com/"></soap:body>
</output>
</operation>
<operation name="uploadImage">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal" namespace="http://ws.mkyong.com/">
</soap:body>
</input>
<output>
<soap:body use="literal" namespace="http://ws.mkyong.com/">
</soap:body>
</output>
</operation>
</binding>
<service name="ImageServerImplService">
<port name="ImageServerImplPort" binding="tns:ImageServerImplPortBinding">
<soap:address location="http://localhost:9999/ws/image"></soap:address>
</port>
</service>
</definitions>
下载源代码
Download It – JAX-WS-Attachment-MTOM-Example.zip (20KB)
参考
- http://www.devx.com/xml/Article/34797/1763/page/1
- http://download . Oracle . com/docs/CD/e 17802 _ 01/web services/web services/docs/2.0/jaxws/mtom-swaref . html
- http://www.crosschecknet.com/intro_to_mtom.php
- http://download . Oracle . com/docs/CD/e 12840 _ 01/WLS/docs 103/webserv _ adv/mtom . html
- http://www . the server side . com/news/1363957/Sending-Attachments-with-SOAP
- http://metro.java.net/guide/Binary_Attachments__MTOM_.html
Tags : attachment jax-ws mtom web services