Some refactoring and added some JUnit tests
This commit is contained in:
parent
d107cd504c
commit
24c4fac26d
13 changed files with 168 additions and 100 deletions
|
|
@ -31,24 +31,18 @@ package zutil.net.torrent;
|
|||
*/
|
||||
public class TorrentFile{
|
||||
private String filename;
|
||||
private long length;
|
||||
private long size;
|
||||
|
||||
public TorrentFile(String filename, long length){
|
||||
public TorrentFile(String filename, long size){
|
||||
this.filename = filename;
|
||||
this.length = length;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,13 @@ import zutil.parser.DataNode;
|
|||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class TorrentMetainfo {
|
||||
/** Name **/
|
||||
private String name;
|
||||
/** Comment (optional) **/
|
||||
private String comment;
|
||||
/** Signature of the software which created the torrent (optional) **/
|
||||
|
|
@ -63,16 +66,16 @@ public class TorrentMetainfo {
|
|||
|
||||
|
||||
|
||||
public TorrentMetainfo(File torrent) throws IOException{
|
||||
public TorrentMetainfo(File torrent) throws IOException, ParseException {
|
||||
this(FileUtil.getContent(torrent));
|
||||
}
|
||||
|
||||
public TorrentMetainfo(String data){
|
||||
public TorrentMetainfo(String data) throws ParseException {
|
||||
decode(data);
|
||||
}
|
||||
|
||||
|
||||
private void decode(String data){
|
||||
private void decode(String data) throws ParseException {
|
||||
DataNode metainfo = BEncodedParser.read(data);
|
||||
|
||||
// Main values
|
||||
|
|
@ -91,66 +94,70 @@ public class TorrentMetainfo {
|
|||
|
||||
// info data
|
||||
DataNode info = metainfo.get("info");
|
||||
String name = info.getString("name");
|
||||
name = info.getString("name");
|
||||
piece_length = info.getLong("piece length");
|
||||
if( info.get("private") != null )
|
||||
is_private = (info.getInt("private") != 0);
|
||||
// Split the hashes
|
||||
String hashes = info.getString("pieces");
|
||||
piece_hashes = new ArrayList<String>();
|
||||
for(int i=0; i<hashes.length() ;i++){
|
||||
String hash = "";
|
||||
for(; i%20!=0 ;i++)
|
||||
hash += hashes.charAt(i);
|
||||
piece_hashes.add(hash);
|
||||
for(int i=0; i<hashes.length(); ){
|
||||
StringBuilder hash = new StringBuilder(20);
|
||||
for(int k=0; k<20; ++i, ++k)
|
||||
hash.append(hashes.charAt(i));
|
||||
piece_hashes.add(hash.toString());
|
||||
}
|
||||
|
||||
// File data
|
||||
file_list = new ArrayList<TorrentFile>();
|
||||
// Single-file torrent
|
||||
if( info.get("files") == null ){
|
||||
Long length = info.getLong("length");
|
||||
file_list.add( new TorrentFile(name, length) );
|
||||
Long fileSize = size = info.getLong("length");
|
||||
file_list.add( new TorrentFile(name, fileSize) );
|
||||
}
|
||||
// Multi-file torrent
|
||||
else{
|
||||
DataNode files = info.get("files");
|
||||
for( DataNode file : files ){
|
||||
String filename = "";
|
||||
StringBuilder filename = new StringBuilder();
|
||||
DataNode tmp = file.get("path");
|
||||
// File in subdir
|
||||
if( tmp.isList() ){
|
||||
Iterator<DataNode> it = tmp.iterator();
|
||||
while( it.hasNext() ){
|
||||
filename += it.next().getString();
|
||||
if(it.hasNext()) filename += File.separator;
|
||||
filename.append(it.next().getString());
|
||||
if(it.hasNext())
|
||||
filename.append(File.separator);
|
||||
}
|
||||
}
|
||||
// File in root dir
|
||||
else
|
||||
filename = tmp.getString();
|
||||
Long length = file.getLong("length");
|
||||
filename = name + File.separator + filename;
|
||||
file_list.add( new TorrentFile(filename, length) );
|
||||
filename.append(tmp.getString());
|
||||
Long fileSize = file.getLong("length");
|
||||
size += fileSize;
|
||||
file_list.add( new TorrentFile(filename.toString(), fileSize) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
public String getCreated_by() {
|
||||
public String getCreatedBy() {
|
||||
return created_by;
|
||||
}
|
||||
public long getCreation_date() {
|
||||
public long getCreationDate() {
|
||||
return creation_date;
|
||||
}
|
||||
public String getAnnounce() {
|
||||
return announce;
|
||||
}
|
||||
public ArrayList<String> getAnnounce_list() {
|
||||
public ArrayList<String> getAnnounceList() {
|
||||
return announce_list;
|
||||
}
|
||||
public String getEncoding() {
|
||||
|
|
@ -159,31 +166,17 @@ public class TorrentMetainfo {
|
|||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
public long getPiece_length() {
|
||||
public long getPieceLength() {
|
||||
return piece_length;
|
||||
}
|
||||
public ArrayList<String> getPiece_hashes() {
|
||||
public ArrayList<String> getPieceHashes() {
|
||||
return piece_hashes;
|
||||
}
|
||||
public boolean isIs_private() {
|
||||
public boolean isPrivate() {
|
||||
return is_private;
|
||||
}
|
||||
public ArrayList<TorrentFile> getFile_list() {
|
||||
public ArrayList<TorrentFile> getFileList() {
|
||||
return file_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example use
|
||||
*/
|
||||
public static void main(String[] args){
|
||||
try {
|
||||
TorrentMetainfo tmp = new TorrentMetainfo(FileUtil.find("test.torrent"));
|
||||
MultiPrintStream.out.dump(tmp);
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface
|
|||
}
|
||||
|
||||
|
||||
@WSDisabled
|
||||
@WSIgnore
|
||||
public void respond(HttpPrintStream out, HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
|
|
|
|||
12
src/zutil/net/ws/WSInterface.java
Normal file → Executable file
12
src/zutil/net/ws/WSInterface.java
Normal file → Executable file
|
|
@ -30,14 +30,16 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
/**
|
||||
*
|
||||
* Specifies web service parameter names and other things.
|
||||
* Specifies web service definitions. Which methods that will
|
||||
* be published and other related metadata.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* private static class Test implements WSInterface{
|
||||
* public Test(){}
|
||||
*
|
||||
* @WSDocumentation("blabla")
|
||||
* @WSDLParamDocumentation("olle = an variable?")
|
||||
* @WSDLParamDocumentation("olle = a variable?")
|
||||
* public void pubZ(
|
||||
* @WSParamName("olle") int lol)
|
||||
* throws Exception{
|
||||
|
|
@ -51,7 +53,7 @@ import java.lang.annotation.Target;
|
|||
* ....
|
||||
* }
|
||||
*
|
||||
* @WSDisabled()
|
||||
* @WSIgnore()
|
||||
* public void privaZ(....){
|
||||
* ...
|
||||
* }
|
||||
|
|
@ -87,13 +89,13 @@ public interface WSInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Disables publication of the given method
|
||||
* Skipp publication of the given method
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface WSDisabled { }
|
||||
public @interface WSIgnore { }
|
||||
|
||||
/**
|
||||
* Method or Parameter comments for the WSDL.
|
||||
|
|
|
|||
12
src/zutil/net/ws/WSMethodDef.java
Normal file → Executable file
12
src/zutil/net/ws/WSMethodDef.java
Normal file → Executable file
|
|
@ -228,16 +228,8 @@ public class WSMethodDef {
|
|||
* @param obj the object the method will called on
|
||||
* @param params a vector with arguments
|
||||
*/
|
||||
public Object invoke(Object obj, Object[] params) throws Throwable{
|
||||
try {
|
||||
return this.method.invoke(obj, params );
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
public Object invoke(Object obj, Object[] params) throws Exception {
|
||||
return this.method.invoke(obj, params );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
2
src/zutil/net/ws/WebServiceDef.java
Normal file → Executable file
2
src/zutil/net/ws/WebServiceDef.java
Normal file → Executable file
|
|
@ -57,7 +57,7 @@ public class WebServiceDef {
|
|||
for(Method m : intf.getDeclaredMethods()){
|
||||
// check for public methods
|
||||
if((m.getModifiers() & Modifier.PUBLIC) > 0 &&
|
||||
!m.isAnnotationPresent(WSInterface.WSDisabled.class)){
|
||||
!m.isAnnotationPresent(WSInterface.WSIgnore.class)){
|
||||
WSMethodDef method = new WSMethodDef(this, m);
|
||||
methods.put(method.getName(), method);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class RestHttpPage implements HttpPage {
|
|||
}
|
||||
|
||||
|
||||
private String execute(String targetMethod, Map<String, String> input) throws Throwable {
|
||||
protected String execute(String targetMethod, Map<String, String> input) throws Exception {
|
||||
if( wsDef.hasMethod(targetMethod) ){
|
||||
// Parse request
|
||||
WSMethodDef m = wsDef.getMethod(targetMethod);
|
||||
|
|
@ -82,6 +82,7 @@ public class RestHttpPage implements HttpPage {
|
|||
// Generate Response
|
||||
StringOutputStream dummyOut = new StringOutputStream();
|
||||
JSONObjectOutputStream out = new JSONObjectOutputStream(dummyOut);
|
||||
out.enableMetaData(false);
|
||||
out.writeObject(ret);
|
||||
out.close();
|
||||
return dummyOut.toString();
|
||||
|
|
@ -103,8 +104,4 @@ public class RestHttpPage implements HttpPage {
|
|||
}
|
||||
return inputParams;
|
||||
}
|
||||
|
||||
private void generateResponse(){
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,19 +40,6 @@ import java.util.logging.Logger;
|
|||
public class SOAPClientFactory {
|
||||
private static Logger logger = LogUtil.getLogger();
|
||||
|
||||
/**
|
||||
* Generates a Client Object for the web service.
|
||||
*
|
||||
* @param <T> is the class of the web service definition
|
||||
* @param url is the target service url
|
||||
* @param intf is the class of the web service definition
|
||||
* @return a client Object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T createClient(URL url, Class<T> intf){
|
||||
return createClient(url, intf,
|
||||
new WebServiceDef((Class<? extends WSInterface>)intf) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Client Object for the web service.
|
||||
|
|
@ -60,12 +47,11 @@ public class SOAPClientFactory {
|
|||
* @param <T> is the class of the web service definition
|
||||
* @param url is the target service url
|
||||
* @param intf is the class of the web service definition
|
||||
* @param wsDef is the web service definition of the intf parameter
|
||||
* @return a client Object
|
||||
*/
|
||||
public static <T> T createClient(URL url, Class<T> intf, WebServiceDef wsDef){
|
||||
public static <T extends WSInterface> T createClient(URL url, Class<T> intf){
|
||||
T obj = WSClientFactory.createClient( intf,
|
||||
new SOAPClientInvocationHandler(url, wsDef));
|
||||
new SOAPClientInvocationHandler(url, new WebServiceDef(intf)));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ package zutil.parser;
|
|||
import zutil.parser.DataNode.DataType;
|
||||
import zutil.struct.MutableInt;
|
||||
|
||||
import java.nio.charset.MalformedInputException;
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* http://wiki.theory.org/BitTorrentSpecification
|
||||
* @author Ziver
|
||||
|
|
@ -40,7 +43,7 @@ public class BEncodedParser {
|
|||
* @param data is the data to be decoded
|
||||
* @return
|
||||
*/
|
||||
public static DataNode read(String data){
|
||||
public static DataNode read(String data) throws ParseException {
|
||||
return decode_BEncoded(new MutableInt(), new StringBuilder(data));
|
||||
}
|
||||
|
||||
|
|
@ -51,9 +54,10 @@ public class BEncodedParser {
|
|||
* @param index is the index in data to start from
|
||||
* @return
|
||||
*/
|
||||
private static DataNode decode_BEncoded(MutableInt index, StringBuilder data){
|
||||
private static DataNode decode_BEncoded(MutableInt index, StringBuilder data) throws ParseException {
|
||||
String tmp;
|
||||
char c = ' ';
|
||||
int end;
|
||||
|
||||
switch (data.charAt(index.i)) {
|
||||
/**
|
||||
|
|
@ -63,7 +67,10 @@ public class BEncodedParser {
|
|||
*/
|
||||
case 'i':
|
||||
index.i++;
|
||||
tmp = data.substring(index.i, data.indexOf("e"));
|
||||
end = data.indexOf("e", index.i);
|
||||
if (end < 0)
|
||||
throw new ParseException("Corrupt bEncoding", index.i);
|
||||
tmp = data.substring(index.i, end);
|
||||
index.i += tmp.length() + 1;
|
||||
return new DataNode( new Long(tmp));
|
||||
/**
|
||||
|
|
@ -97,6 +104,8 @@ public class BEncodedParser {
|
|||
while(c != 'e'){
|
||||
DataNode tmp2 = decode_BEncoded(index, data);
|
||||
map.set(tmp2.getString(), decode_BEncoded(index, data));
|
||||
if (index.i >= data.length())
|
||||
throw new ParseException("Incorrect bEncoding ending of element", index.i);
|
||||
c = data.charAt(index.i);
|
||||
}
|
||||
index.i++;
|
||||
|
|
@ -107,10 +116,13 @@ public class BEncodedParser {
|
|||
* would bEncode to 11:BitTorrents.
|
||||
*/
|
||||
default:
|
||||
tmp = data.substring(index.i, data.indexOf(":"));
|
||||
end = data.indexOf(":", index.i);
|
||||
if (end < 0)
|
||||
throw new ParseException("Corrupt bEncoding", index.i);
|
||||
tmp = data.substring(index.i, end);
|
||||
int length = Integer.parseInt(tmp);
|
||||
index.i += tmp.length() + 1;
|
||||
String ret = data.substring(index.i, length);
|
||||
String ret = data.substring(index.i, index.i+length);
|
||||
index.i += length;
|
||||
return new DataNode( ret );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,10 +200,16 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput
|
|||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
super.flush();
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override public void writeBoolean(boolean v) throws IOException {
|
||||
|
|
|
|||
41
test/zutil/net/torrent/TorrentMetainfoTest.java
Executable file
41
test/zutil/net/torrent/TorrentMetainfoTest.java
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by Ziver on 2016-09-27.
|
||||
*/
|
||||
public class TorrentMetainfoTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void decode() throws ParseException {
|
||||
TorrentMetainfo tmp = new TorrentMetainfo(
|
||||
"d8:announce39:http://torrent.ubuntu.com:6969/announce13:announce-listll" +
|
||||
"39:http://torrent.ubuntu.com:6969/announceel44:http://ipv6.torrent.ubuntu.com:6969/announceee" +
|
||||
"7:comment29:Ubuntu CD releases.ubuntu.com13:creation datei1469103218e4:infod" +
|
||||
"6:lengthi1513308160e4:name32:ubuntu-16.04.1-desktop-amd64.iso12:piece lengthi524288e" +
|
||||
"6:pieces60:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaee");
|
||||
|
||||
assertEquals("http://torrent.ubuntu.com:6969/announce", tmp.getAnnounce());
|
||||
assertArrayEquals(new String[]{
|
||||
"http://torrent.ubuntu.com:6969/announce",
|
||||
"http://ipv6.torrent.ubuntu.com:6969/announce"},
|
||||
tmp.getAnnounceList().toArray());
|
||||
assertEquals("Ubuntu CD releases.ubuntu.com", tmp.getComment());
|
||||
assertEquals(1469103218, tmp.getCreationDate());
|
||||
assertEquals(1513308160, tmp.getSize());
|
||||
assertEquals("ubuntu-16.04.1-desktop-amd64.iso", tmp.getName());
|
||||
assertEquals(1, tmp.getFileList().size());
|
||||
assertEquals("ubuntu-16.04.1-desktop-amd64.iso", tmp.getFileList().get(0).getFilename());
|
||||
assertEquals(524288, tmp.getPieceLength());
|
||||
assertEquals(3, tmp.getPieceHashes().size());
|
||||
assertEquals("aaaaaaaaaaaaaaaaaaaa", tmp.getPieceHashes().get(0));
|
||||
assertEquals("aaaaaaaaaaaaaaaaaaaa", tmp.getPieceHashes().get(1));
|
||||
assertEquals("aaaaaaaaaaaaaaaaaaaa", tmp.getPieceHashes().get(2));
|
||||
}
|
||||
}
|
||||
49
test/zutil/net/ws/rest/RestHttpPageTest.java
Executable file
49
test/zutil/net/ws/rest/RestHttpPageTest.java
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
package zutil.net.ws.rest;
|
||||
|
||||
import org.junit.Test;
|
||||
import zutil.net.ws.WSInterface;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by Ziver on 2016-09-27.
|
||||
*/
|
||||
public class RestHttpPageTest {
|
||||
|
||||
public static class TestClass implements WSInterface{
|
||||
public String hello(){
|
||||
return "hello world";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noInput() throws Throwable {
|
||||
RestHttpPage rest = new RestHttpPage(new TestClass());
|
||||
|
||||
HashMap<String,String> input = new HashMap<>();
|
||||
String output = rest.execute("hello", input);
|
||||
assertEquals("\"hello world\"\n", output);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static class TestEchoClass implements WSInterface{
|
||||
public String echo(@WSParamName("input") String input){
|
||||
return "echo: "+input;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneInput() throws Throwable {
|
||||
RestHttpPage rest = new RestHttpPage(new TestEchoClass());
|
||||
|
||||
HashMap<String,String> input = new HashMap<>();
|
||||
input.put("input", "test input");
|
||||
String output = rest.execute("echo", input);
|
||||
assertEquals("\"echo: test input\"\n", output);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,10 +38,6 @@ import zutil.parser.wsdl.WSDLWriter;
|
|||
public class SOAPTest {
|
||||
/************************* TEST CASES ************************/
|
||||
public static void main(String[] args){
|
||||
new SOAPTest();
|
||||
}
|
||||
|
||||
public SOAPTest(){
|
||||
WebServiceDef wsDef = new WebServiceDef( MainSOAPClass.class );
|
||||
SOAPHttpPage soap = new SOAPHttpPage( wsDef );
|
||||
|
||||
|
|
@ -84,7 +80,7 @@ public class SOAPTest {
|
|||
public static class SpecialReturnClass extends WSReturnObject{
|
||||
@WSValueName(value="otherValue1")
|
||||
public String param1 = "otherValue1";
|
||||
@WSValueName("otherValue2")
|
||||
@WSValueName("otherName2")
|
||||
public String param2 = "otherValue2";
|
||||
public byte[] b = new byte[]{0x12, 0x23};
|
||||
public InnerClass inner = new InnerClass();
|
||||
|
|
@ -147,7 +143,7 @@ public class SOAPTest {
|
|||
@WSParamDocumentation("void method documentation")
|
||||
public void voidMethod (){ }
|
||||
|
||||
@WSDisabled()
|
||||
@WSIgnore()
|
||||
public void disabledMethod(){ }
|
||||
protected void protectedMethod(){ }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue