Mkyong 中文博客翻译(三十二)

原文:Mkyong

协议:CC BY-NC-SA 4.0

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来获取所有的组,我们需要一个自定义的Spliteratorshack

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 脚本

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/java-run-shell-script-on-a-remote-server/

本文展示了如何使用 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 异常:找不到与本地主机匹配的名称

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/webservices/jax-ws/java-security-cert-certificateexception-no-name-matching-localhost-found/

问题

配置了 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 序列化

在 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 序列化的一些用例:

  1. 套接字、应用程序、客户端和服务器之间的数据交换格式。它喜欢 JSON 或 XML,但采用字节格式。
  2. 序列化是几项关键 Java 技术的基础,包括 Java 远程方法调用(Java RMI) 、公共对象请求代理架构(CORBA),以及分布式计算,如 Java 命名和目录接口(JNDI)、Java
    管理扩展(JMX)和 Java 消息传递(JMS)。
  3. 对于作为轻量级持久性的游戏,我们可以在磁盘上序列化当前游戏的状态,并在以后恢复它。文件是字节格式的,我们不能轻易修改敏感游戏的数据,比如武器或者金钱。
  4. 保存一个对象图磁盘用于进一步分析,比如 UML 类图。
  5. 服务器集群和恢复会话。例如,如果其中一个服务器关闭或重启,服务器集群(服务器 A 和服务器 B)需要同步会话。当服务器 A 关闭时,它将对象保存为HttpSession的属性,并通过网络发送给服务器 B,服务器 B 可以从HttpSession恢复对象。阅读这篇的帖子

对于数据交换,考虑人类可读的数据交换格式,如 JSONXMLGoogle 协议缓冲区

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.sql.SQLException:不允许操作:序号绑定和命名绑定不能组合!

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/jdbc/java-sql-sqlexception-operation-not-allowed-ordinal-binding-and-named-binding-cannot-be-combined/

顺序绑定或索引绑定:

 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”

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/jdbc/java-sql-sqlexception-the-server-time-zone-value-xx-time-is-unrecognized/

使用最新的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.inimy.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.inimy.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();
	} 

参考

Tags : java jdbc mysql

Java–流已经被操作或关闭

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java8/java-stream-has-already-been-operated-upon-or-closed/

在 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?

参考

  1. 供应商 JavaDoc
  2. 流摘要 JavaDoc

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(流)。

目录

DOM、SAX 和 StAX 都是 Java APIs 的一部分。

1。文档对象模型

文档对象模型(DOM) 使用节点将整个 XML 文档表示为一个树形结构,并将它们存储在内存中。

DOM 有利于操纵小的 XML 文件,比如读、写和修改 XML 结构;DOM 不用于解析或操作大型 XML 文件,因为在内存中构建整个 XML 结构会消耗大量内存。

DOM 解析器示例

  1. DOM–读取 XML
  2. DOM–编写 XML
  3. DOM–修改 XML
  4. DOM–漂亮的打印 XML
  5. DOM–XML 和 XSLT

2。XML 的简单应用编程接口(SAX)

Simple API for XML (SAX)是一个流模型、事件驱动、推送解析 API,用于读取 XML 文档(需要另一个 API 来编写)。SAX 从头到尾读取 XML 文件,当遇到一个元素时调用一个方法,或者当找到特定的文本或属性时调用另一个方法。

SAX 快速高效,比 DOM 需要更少的内存,因为 SAX 不像 DOM 那样创建 XML 数据的内部表示(树结构)。

SAX 解析器示例

  1. SAX–读取 XML
  2. SAX–读取 XML UTF-8

3。XML 流应用编程接口(StAX)

XML 流 API(StAX)是一个流模型、事件驱动、用于读写 XML 文档的拉解析 API。StAX 提供了比 SAX 更简单的编程模型和比 DOM 更高效的内存管理。

StAX 解析器示例

  1. StAX–读取 XML
  2. StAX–编写 XML

4。第三方 XML 解析器(JDOM)

DOM、SAX 和 StAX 是 Java APIs 的一部分。然而,原料药可能不适合每个人的口味。或者,我们可以使用第三方 XML 解析器:

JDOM 解析器示例

  1. JDOM–读取 XML
  2. JDOM–编写 XML
  3. JDOM–修改 XML

5。用于 XML 绑定的 Java 架构(JAXB)

Jakarta XML 绑定(JAXB;以前的 Java Architecture for XML Binding)是一个 XML 绑定框架,用于在 Java 类和 XML 之间进行转换。

JAXB 示例

  1. JAXB 历史和 hello world 示例

6。Java XML 常见问题解答

一些常见问题。

  1. XML 外部实体(XXE)
  2. 将 XML 转换为属性
  3. 将属性转换为 XML
  4. 计算 XML 元素数量
  5. 计算 XML 的深度
  6. 将字符串转换成 XML

7。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-xml

$ CD src/main/Java/com/mkyong/XML/

8。参考文献

javax . naming . namenotfoundexception:名称 jdbc 未在此上下文中绑定

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/javax-naming-namenotfoundexception-name-jdbc-is-not-bound-in-this-context/

问题

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 是一个受限类

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/google-app-engine/javax-swing-tree-treenode-is-a-restricted-class/

问题

在以下环境中,在 Google App Engine 上开发 Struts2。

  1. struts 2.3.1.2
  2. freemaker 2.3.18
  3. JDK 1.6
  4. Eclipse 3.7+Eclipse 的 Google 插件
  5. 谷歌应用引擎 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;
	}
} 

参考

  1. Freemarker 2.4,GAE/J 1.2.6 和 javax.swing.tree.TreeNode

gae struts2

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 教程中,我们同时使用了 JerseyRESTEasy ,流行的 JAX-RS 实现。

快乐学习 JAX🙂

快速启动

一些快速使用 JAX 遥感器的例子。

基本示例

开发 REST 服务的基本注释和函数。

文件上传示例

如何处理 JAX 遥感中的多部分数据?

使用 XML

JAX 遥感系统中的 XML 支持。

使用 JSON

JAX 的 JSON 支持。

RESTful Java 客户端

创建一个 RESTful Java 客户端来执行“GET”和“POST”请求,以操作 json 数据。

JAX-遥感+春天

将 JAX 遥感系统与 Spring 框架集成。

常见错误消息

JAX 遥感器开发中的一些常见错误信息。

参考

  1. 球衣官网
  2. 球衣用户指南
  3. RESTEasy 官网
  4. RESTEasy 用户指南
  5. 维客休息解释

JAX-WS 与 MTOM 的关系

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/webservices/jax-ws/jax-ws-attachment-with-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)

参考

  1. http://www.devx.com/xml/Article/34797/1763/page/1
  2. http://download . Oracle . com/docs/CD/e 17802 _ 01/web services/web services/docs/2.0/jaxws/mtom-swaref . html
  3. http://www.crosschecknet.com/intro_to_mtom.php
  4. http://download . Oracle . com/docs/CD/e 12840 _ 01/WLS/docs 103/webserv _ adv/mtom . html
  5. http://www . the server side . com/news/1363957/Sending-Attachments-with-SOAP
  6. http://metro.java.net/guide/Binary_Attachments__MTOM_.html

Tags : attachment jax-ws mtom web services

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值