定时执行任务的三种方法
|
|
09:34
|
|
小小豆叮
09:34
|
|
小小豆叮
09:34
|
|
小小豆叮
09:34
|
|
小小豆叮
09:33
|
|
小小豆叮
09:32
|
|
小小豆叮
09:32
|
|
小小豆叮
09:32
|
|
小小豆叮
09:32
|
|
小小豆叮
09:32
Title: System
* *Description:
* *Copyright: Copyright (c) 2005
* *Company: my
* * @author microapp * @version 1.0 */ public interface IHSM { /** * 加密数据 * @param interspersed * @param data String * @param index int * @return String * @throws HSMException */ public String getEncryption(String interspersed, String data, int index) throws HSMException; /** * getKey * * @param inInterspersed String * @param outInterspersed String * @param index int */ public String getKey(String inInterspersed, String outInterspersed, int index) throws HSMException; /** * 解密数据 * @param interspersed * @param data String * @param index int * @return String * @throws HSMException */ public String getDecryption(String interspersed, String data, int index) throws HSMException; /** * 取得MAC * @param interspersed * @param challenge String * @param data String * @return String * @throws HSMException */ public String getMAC(String interspersed, String data, int index) throws HSMException; } 异常类: ackage com.microapp.business.hsm; /** *Title:
* *Description:
* *Copyright: Copyright (c) 2005
* *Company:
* * @author microapp * @version 1.0 */ public class HSMException extends Exception { String message; public HSMException() { } public HSMException(String errmsg) { super(errmsg); } public HSMException(int errCode) { switch(errCode) { case 80: message - "命令不可识别!"; break; case 81: message - "报文长度错误!"; break; default: message - "其他错误!"; } } } 实现文件 package com.microapp.business.hsm; /** *Title:
* *Description:
* *Copyright: Copyright (c) 2005
* *Company:
* * @author me * @version 1.0 */ import java.io.*; import java.net.*; public class HSM implements IHSM { private String address = "192.168.1.88"; private int port = 8888; public HSM() { } public HSM(String addr, int port) { this.address = addr; this.port = port; } public void setAddress(String addr) { this.address = addr; } public String getAddress() { return address; } public void setPort(int port) { this.port = port; } public int getPort() { return port; } /** * 加密 * @param interspersed String * @param data String * @param index int * @return String * @throws HSMException */ public String getEncryption(String interspersed, String data, int index) throws HSMException { System.out.println("Hava the data request at HSMe " + data); int l = data.length(); if(l != 16) { throw new HSMException("Data length error!"); } Socket Client; DataInputStream InputS; DataInputStream KeyS; PrintStream OutputS; byte[] sendbuf = new byte[45]; byte[] recvbuf = new byte[64]; int i = 0; byte b1 = 0; byte b2 = 0; try { Client = new Socket(address, port); InputS = new DataInputStream(Client.getInputStream()); OutputS = new PrintStream(Client.getOutputStream()); KeyS = new DataInputStream(System.in); sendbuf[0] = 0; //长度(高) sendbuf[1] = 45; //长度(低) sendbuf[2] = (byte)0xB0; //命令代码 sendbuf[3] = 1; //密钥索引 sendbuf[4] = 24; //密钥长度 sendbuf[5] = 0; //密钥 for(i = 0; i < 24; i++) { sendbuf[5 + i] =(byte)(Integer.parseInt(interspersed.substring((i * 2), (i * 2) +2), 16)); } sendbuf[6] = 0x80; //计算方式:80-加密 sendbuf[7] = 0x00; //计算数据长度(高) sendbuf[8] = 8; //计算数据长度(低) sendbuf[9] = 0; //计算数据 for(i = 0; i < 8; i++) { sendbuf[9 + i] =(byte)(Integer.parseInt(data.substring((i * 2), (i * 2) +2), 16)); } OutputS.write(sendbuf); l = InputS.read(recvbuf); Client.close(); if (l < 6) { throw new HSMException("Return data length error." + Integer.toHexString(l)); } if((recvbuf[3] != 0)) { throw new HSMException("Return data error." + Integer.toHexString(recvbuf[3]).toUpperCase()); } l = (recvbuf[4] * 256 + recvbuf[5]); StringBuffer sb = new StringBuffer(); String s = new String(); for(i = 0; i < l; i++) { s = Integer.toHexString(recvbuf[i + 6]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(IOException e) { throw new HSMException("IOException Happened" + e.getMessage()); } } /** * 解密 * @param interspersed String * @param data String * @return String * @throws HSMException */ public String getDecryption(String interspersed, String data, int index) throws HSMException { System.out.println("Hava the data request at HSMd " + data); int l = data.length(); if((l % 2) != 0) { throw new HSMException("Data length error!"); } Socket Client; DataInputStream InputS; DataInputStream KeyS; PrintStream OutputS; byte[] sendbuf = new byte[45]; byte[] recvbuf = new byte[64]; int i = 0; byte b1 = 0; byte b2 = 0; try { Client = new Socket(address, port); InputS = new DataInputStream(Client.getInputStream()); OutputS = new PrintStream(Client.getOutputStream()); KeyS = new DataInputStream(System.in); sendbuf[0] = 0; //长度(高) sendbuf[1] = 45; //长度(低) sendbuf[2] = (byte)0xB0; //命令代码 sendbuf[3] = 1; //密钥索引 sendbuf[4] = 24; //密钥长度 sendbuf[5] = 0; //密钥 for(i = 0; i < 24; i++) { sendbuf[5 + i] =(byte)(Integer.parseInt(interspersed.substring((i * 2), (i * 2) +2), 16)); } sendbuf[6] = 0x81; //计算方式:81-解密 sendbuf[7] = 0x00; //计算数据长度(高) sendbuf[8] = (byte)(l / 2); //计算数据长度(低) sendbuf[9] = 0; //计算数据 for(i = 0; i < (l / 2); i++) { sendbuf[9 + i] =(byte)(Integer.parseInt(data.substring((i * 2), (i * 2) +2), 16)); } OutputS.write(sendbuf); i = InputS.read(recvbuf); Client.close(); if(i < 6) { throw new HSMException("Return data length error." + Integer.toHexString(i)); } if((recvbuf[3] != 0)) { throw new HSMException("Return data error." + Integer.toHexString(recvbuf[3]).toUpperCase()); } l = (recvbuf[4] * 256 + recvbuf[5]); StringBuffer sb = new StringBuffer(); String s = new String(); for(i = 0; i < l; i++) { s = Integer.toHexString(recvbuf[i + 6]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(IOException e) { throw new HSMException("IOException Happened" + e.getMessage()); } } /** * 获取MAC * @param interspersed String * @param challenge String :随机数 * @param data String :加密数据 * @return String * @throws HSMException */ public String getMAC(String interspersed, String data, int index) throws HSMException { Socket Client; DataInputStream InputS; DataInputStream KeyS; PrintStream OutputS; byte[] sendbuf = new byte[64]; byte[] recvbuf = new byte[64]; int i = 0; byte b1 = 0; byte b2 = 0; try { int l = data.length(); if ((l % 2) != 0) { throw new HSMException("Data length must be 2 times."); } Client = new Socket(address, port); InputS = new DataInputStream(Client.getInputStream()); OutputS = new PrintStream(Client.getOutputStream()); KeyS = new DataInputStream(System.in); sendbuf[0] = 0x00; //长度 sendbuf[1] = (byte) (l + 12); //长度 sendbuf[2] = (byte) 0xB0; //命令代码 sendbuf[3] = 1; //密钥索引 sendbuf[4] = 24; //密钥长度 sendbuf[5] = 0; //密钥 for(i = 0; i < 24; i++) { sendbuf[5 + i] =(byte)(Integer.parseInt(interspersed.substring((i * 2), (i * 2) +2), 16)); } sendbuf[6] = 0x82; //计算方式:82-计算MAC sendbuf[7] = 0; //计算数据长度 sendbuf[8] = (byte) i; //计算数据长度 sendbuf[9] = 0; //计算数据 for (i = 0; i < (l / 2); i++) { sendbuf[37 + i] =(byte)(Integer.parseInt(data.substring((i * 2), (i * 2) +2), 16)); } OutputS.write(sendbuf); i = InputS.read(recvbuf); Client.close(); if (i < 6) { throw new HSMException("Return data length error." + Integer.toHexString(i)); } if ( (recvbuf[3] != 0)) { throw new HSMException("Return data error." + Integer.toHexString(recvbuf[3]).toUpperCase()); } l = (recvbuf[4] * 256 + recvbuf[5]); StringBuffer sb = new StringBuffer(); String s = new String(); for (i = 0; i < l; i++) { s = Integer.toHexString(recvbuf[i + 6]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch (IOException e) { throw new HSMException("IOException Happened" + e.getMessage()); } } } 为在应用服务器中能够实现逻辑数据组合,我建立了一个无状态session bean.在bean主要是实现数据的打包和转换. package com.microapp.business; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.ejb.CreateException; import com.vsc.ets.business.hsm.*; import java.util.Random; public class HSMModuleBean implements SessionBean { public static final int EXTERNAL_AUTHEN_KEY = 0x46; public static final int INTERNAL_AUTHEN_KEY = 0x45; public static final int MAC_KEY = 0x44; public static final int GENERAL_KEY = 0x41; SessionContext sessionContext; IHSM hsm = new HSM("192.168.1.88", 8888); Random rd = new Random(); String challenge; public void ejbCreate() throws CreateException { } public void ejbRemove() { } public void ejbActivate() { } public void ejbPassivate() { } public void setSessionContext(SessionContext sessionContext) { this.sessionContext = sessionContext; } public String getMAC(String s1, String s2, String s3, String challenge, String data) { try { String interspersed = s1 + s2 + s3; return hsm.getMAC(interspersed, challenge, MAC_KEY); } catch (Exception ex) { return null; } } public boolean internalAuthentication(String s1, String s2, String s3, String data) { String interspersed = s1 + s2 + s3; try { String s = hsm.getDecryption(interspersed, data, INTERNAL_AUTHEN_KEY); System.out.println("HSM internal authentication: " + s + " challenge: " + challenge); if(s.equals(challenge)) { return true; } else { return false; } } catch(HSMException ex) { return false; } } public String[] externalAuthentication(String s1, String s2, String s3, String data) { String interspersed = s1 + s2 + s3; String[] s = new String[2]; this.challenge = "1234567887654321"; try { s[0] = hsm.getEncryption(interspersed, data, EXTERNAL_AUTHEN_KEY); s[1] = this.challenge; System.out.println("Ext: Encryption : " + s[0] + " Challenge: " + s[1]); return s; } catch(HSMException ex) { return null; } } public String provideEncryption(String interspersed, String data) { try { return hsm.getEncryption(interspersed, data, GENERAL_KEY); } catch (Exception ex) { return null; } } public String provideDecryption(String interspersed, String data) { try { return hsm.getDecryption(interspersed, data, GENERAL_KEY); } catch (Exception ex) { return null; } } } 该过程在实践中应用,完全与硬加密机HSM相同. <淘宝热门商品:
|
|
小小豆叮
09:31
|
|
小小豆叮
09:31
|
|
小小豆叮
09:31
|
|
小小豆叮
09:31
|
|
小小豆叮
09:31
|
|
小小豆叮
09:30
Title: 基于PC/SC标准的读写器实现
* *Description:
* *Copyright: Copyright (c) 2005
* *Company: 我的软件公司
* * @author etd group * @version 1.0 */ import java.util.*; import opencard.core.*; import opencard.core.service.*; import opencard.core.terminal.*; import opencard.core.util.*; import opencard.opt.util.*; public class ReaderMA implements IReader { private static final int IFD_TIMEOUT = 5; // unit: seconds private static final int MAX_APDU_SIZE = 200; // unit: byte private static final int SW1_OK = (byte)0x90; private static final int SW2_OK = 0x00; private static final int RESPONSE_OK = 0x61; private static SmartCard sc = null; private static PassThruCardService ptcs = null; private static CardRequest cr = null; private static CommandAPDU capdu = null; private static ResponseAPDU rapdu = null; byte[] rbuf = null; public ReaderMA() { } /** * 复位 * @throws ReaderPCSCException */ public byte[] Reset() throws ReaderException { try { if (cr == null) { SmartCard.start(); cr = new CardRequest(CardRequest.ANYCARD, null, PassThruCardService.class); } cr.setTimeout(IFD_TIMEOUT); sc = SmartCard.waitForCard(cr); if(sc == null) { throw new ReaderPCSCException("did not get a SmartCard object!"); } CardID cardID = sc.getCardID(); byte[] atr = cardID.getATR(); capdu = new CommandAPDU(MAX_APDU_SIZE); ptcs = (PassThruCardService) sc.getCardService(PassThruCardService.class, true); return atr; } catch(CardTerminalException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } catch(CardServiceException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } catch(ClassNotFoundException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } catch(OpenCardPropertyLoadingException ex) { throw new ReaderException("Reset failed!" + ex.getMessage()); } } /** * 下电 * @throws ReaderPCSCException */ public void ShutDown() throws ReaderException { try { if(sc.isStarted()) { sc.shutdown(); sc.close(); SmartCard.shutdown(); } sc = null; cr = null; } catch(CardTerminalException ex) { throw new ReaderException("Shutdown failed!" + ex.getMessage()); } } /** * 读随机数 * @return byte[] */ public byte[] getChallenge() throws ReaderException { try { byte[] command = new byte[5]; command[0] = 0x00; // CLA command[1] = (byte)0x84; // INS command[2] = 0x00; // P1 command[3] = 0x00; // P2 command[4] = 0x08; // Lc capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if(((response.sw1() == SW1_OK) && (response.sw2() == SW2_OK)) || (response.sw1() == RESPONSE_OK)) { return response.data(); } else { throw new ReaderPCSCException(response.sw1(), response.sw2()); } } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } } /** * SelectTicketApplication * @return boolean */ public boolean SelectApplication() throws ReaderPCSCException { try { byte[] command = new byte[7]; command[0] = 0x00; // CLA command[1] = (byte)0xA4; // INS command[2] = 0x00; // P1 command[3] = 0x00; // P2 command[4] = 0x02; // Lc command[5] = 0x2F; command[6] = 0x01; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if(((response.sw1() == SW1_OK) && (response.sw2() == SW2_OK)) || (response.sw1() == RESPONSE_OK)) { return true; } return false; } catch(OpenCardException ex) { throw new ReaderPCSCException("Select Ticket Application failed!" + ex.getMessage()); } } /** * 外部认证 * @param data String * @return boolean : true: pass, false: error. */ public boolean ExternalAuthentication(byte[] data) throws IndexOutOfBoundsException, NumberFormatException, ReaderPCSCException { try { byte[] command = new byte[13]; command[0] = 0x00; // CLA command[1] = (byte)0x82; // INS command[2] = 0x00; // P1 command[3] = 0x02; // P2 command[4] = 0x08; // Lc command[5] = data[0]; command[6] = data[1]; command[7] = data[2]; command[8] = data[3]; command[9] = data[4]; command[10] = data[5]; command[11] = data[6]; command[12] = data[7]; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.getByte(0), response.getByte(1)); } return true; } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } catch(ReaderPCSCException ex) { throw new ReaderPCSCException(ex.getMessage()); } } /** * 内部认证 * @param data String * @return byte[] */ public byte[] IntenalAuthentication(byte[] data) throws IndexOutOfBoundsException, NumberFormatException, ReaderPCSCException { try { byte[] command = new byte[13]; command[0] = 0x00; // CLA command[1] = (byte)0x88; // INS command[2] = 0x00; // P1 command[3] = 0x01; // P2 command[4] = 0x08; // Lc command[5] = data[0]; command[6] = data[1]; command[7] = data[2]; command[8] = data[3]; command[9] = data[4]; command[10] = data[5]; command[11] = data[6]; command[12] = data[7]; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if(response.sw1() == 0x61) { command = new byte[5]; command[0] = 0x00; // CLA command[1] = (byte)0xC0; // INS command[2] = 0x00; // P1 command[3] = 0x00; // P2 command[4] = 0x08; // Lc capdu.setLength(0); capdu.append(command); response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.sw1(), response.sw2()); } return response.data(); } else { throw new ReaderPCSCException(response.getByte(0), response.getByte(1)); } } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } catch(ReaderPCSCException ex) { throw new ReaderPCSCException(ex.getMessage()); } } /** * 读基本信息文件 * @return byte[] * @throws ReaderPCSCException */ public byte[] ReadBasicFile() throws ReaderPCSCException { try { byte[] command = new byte[5]; command[0] = 0x00; // CLA command[1] = (byte)0xb0; // INS command[2] = (byte)0x95; // P1 command[3] = 0x00; // P2 command[4] = 0x27; // Lc capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.getByte(39), response.getByte(40)); } return response.data(); } catch(OpenCardException ex) { throw new ReaderPCSCException("Read base file failed!" + ex.getMessage()); } } /** * 读持卡人信息文件 * @return String * @throws ReaderPCSCException */ public byte[] ReadHolderFile() throws ReaderPCSCException { return null; } /** * 读基本信息文件 * @param record int * @return String * @throws ReaderPCSCException */ public byte[] ReadBasicInfo(int record) throws ReaderPCSCException { try { byte[] command = new byte[5]; command[0] = 0x00; command[1] = (byte)0xB2; command[2] = (byte)record; command[3] = (byte)0x84; command[4] = (byte)0x9C; capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.sw1(), response.sw2()); } return response.data(); } catch(OpenCardException ex) { throw new ReaderPCSCException("Read ticket file error!" + ex.getMessage()); } } /** * 写基本信息文件 * @param record * @param data String * @throws ReaderPCSCException */ public boolean WriteBasicInfo(int record, byte[] data) throws ReaderPCSCException { try { byte[] command = new byte[161]; command[0] = 0x00; command[1] = (byte)0xDC; command[2] = (byte)record; command[3] = (byte)0x84; command[4] = (byte)0x9C; System.arraycopy(data, 0, command, 5, 0x9C); capdu.setLength(0); capdu.append(command); ResponseAPDU response = ptcs.sendCommandAPDU(capdu); if((response.sw1() != SW1_OK) || (response.sw2() != SW2_OK)) { throw new ReaderPCSCException(response.sw1(), response.sw2()); } return true; } catch(OpenCardException ex) { throw new ReaderPCSCException("Get challenge failed!" + ex.getMessage()); } catch(ReaderPCSCException ex) { throw new ReaderPCSCException(ex.getMessage()); } } 用opencard规范开发非常的简单,特别是处理返回数据,在responseAPDU内用sw1和sw22个方法用于返回sw1,sw2.数据是直接用response.data()即可.省缺了一般读写拼字节的过程. 在读写器之上我定义了一个基于应用的卡片类,AppCard,在这个类内实现卡片的具体操作. 如内部认证,外部认证,获取随机数等. public void reset() throws TicketException { try { byte[] batr = reader.Reset(); StringBuffer sb = new StringBuffer(); String s = new String(); for(int i = 0; i < batr.length; i++) { s = Integer.toHexString(batr[i]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } sb.append(" "); } atr = sb.toString(); } catch(ReaderPCSCException rex) { throw new TicketException("Reset failed! " + rex.getMessage()); } } /** * ShutDown */ public void ShutDown() throws TicketException { try { reader.ShutDown(); } catch(ReaderException ex) { throw new TicketException("Shutdown failed!"); } } /** * 获取随机数 * @return String */ public String getChallenge() throws TicketException { try { StringBuffer sb = new StringBuffer(); String s = new String(); byte[] buf = reader.getChallenge(); for(int i = 0; i < buf.length; i++) { s = Integer.toHexString(buf[i]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(ReaderPCSCException rex) { throw new TicketException(rex.getMessage()); } } /** * 内部认证 * @param data String * @return String */ public String IntenalAuthentication(String data) throws TicketException { try { byte[] sendbuf = new byte[8]; sendbuf[0] = (byte)(Integer.parseInt(data.substring(0,2), 16)); sendbuf[1] = (byte)(Integer.parseInt(data.substring(2,4), 16)); sendbuf[2] = (byte)(Integer.parseInt(data.substring(4,6), 16)); sendbuf[3] = (byte)(Integer.parseInt(data.substring(6,8), 16)); sendbuf[4] = (byte)(Integer.parseInt(data.substring(8,10), 16)); sendbuf[5] = (byte)(Integer.parseInt(data.substring(10,12), 16)); sendbuf[6] = (byte)(Integer.parseInt(data.substring(12,14), 16)); sendbuf[7] = (byte)(Integer.parseInt(data.substring(14,16), 16)); StringBuffer sb = new StringBuffer(); String s = new String(); byte[] recvbuf = reader.IntenalAuthentication(sendbuf); for(int i = 0; i < recvbuf.length; i++) { s = Integer.toHexString(recvbuf[i]).toUpperCase(); if(s.length() == 1) { sb.append("0" + s); } else if(s.length() > 2) { sb.append(s.substring(s.length() - 2, s.length())); } else { sb.append(s); } } return sb.toString(); } catch(ReaderPCSCException rex) { throw new TicketException(rex.getMessage()); } catch(NumberFormatException ex) { throw new TicketException(ex.getMessage()); } catch(IndexOutOfBoundsException ex) { throw new TicketException(ex.getMessage()); } } /** * 外部认证 * * @param data String */ public boolean ExternalAuthentication(String data) throws TicketException { try { byte[] buf = new byte[8]; buf[0] = (byte)(Integer.parseInt(data.substring(0,2), 16)); buf[1] = (byte)(Integer.parseInt(data.substring(2,4), 16)); buf[2] = (byte)(Integer.parseInt(data.substring(4,6), 16)); buf[3] = (byte)(Integer.parseInt(data.substring(6,8), 16)); buf[4] = (byte)(Integer.parseInt(data.substring(8,10), 16)); buf[5] = (byte)(Integer.parseInt(data.substring(10,12), 16)); buf[6] = (byte)(Integer.parseInt(data.substring(12,14), 16)); buf[7] = (byte)(Integer.parseInt(data.substring(14,16), 16)); return reader.ExternalAuthentication(buf); } catch(ReaderPCSCException rex) { throw new TicketException(rex.getMessage()); } catch(NumberFormatException ex) { throw new TicketException(ex.getMessage()); } catch(IndexOutOfBoundsException ex) { throw new TicketException(ex.getMessage()); } } 利用该方法,在客户端实现了用户卡的读写,大大简化了开发,并运行稳定,性能完全预期要求. <淘宝热门商品:
|
|
小小豆叮