Browse Source

merging the My-Shop and WebAPI-Samples repositories

master
Nicolas Massé 10 years ago
parent
commit
51ace85dc5
  1. 1
      My-Shop/README.md
  2. 8
      My-Shop/go-backend/set-go-path.sh
  3. 1
      My-Shop/go-backend/src/.gitignore
  4. 208
      My-Shop/go-backend/src/itix.fr/rest-backend/main.go
  5. 6
      My-Shop/go-backend/test/create-product.sh
  6. 1
      My-Shop/go-backend/www-root/.gitignore
  7. BIN
      My-Shop/go-backend/www-root/book-1234.pdf
  8. BIN
      My-Shop/go-backend/www-root/img/bluray.jpg
  9. BIN
      My-Shop/go-backend/www-root/img/brice.jpg
  10. BIN
      My-Shop/go-backend/www-root/img/cocotte.jpg
  11. BIN
      My-Shop/go-backend/www-root/img/dvd.jpg
  12. BIN
      My-Shop/go-backend/www-root/img/livre.jpg
  13. BIN
      My-Shop/go-backend/www-root/img/logo.png
  14. BIN
      My-Shop/go-backend/www-root/img/mp.jpg
  15. BIN
      My-Shop/go-backend/www-root/img/pull.jpg
  16. BIN
      My-Shop/go-backend/www-root/img/visseuse.jpg
  17. 31
      My-Shop/go-backend/www-root/index.html
  18. 3
      My-Shop/go-backend/www-root/js/.gitignore
  19. 217
      My-Shop/go-backend/www-root/js/shop.js
  20. 218
      My-Shop/go-backend/www-root/style.css
  21. 13
      My-Shop/soap-backend/deploy.sh
  22. 1
      My-Shop/soap-backend/src/fr/itix/soapbackend/.gitignore
  23. 14
      My-Shop/soap-backend/src/fr/itix/soapbackend/VendorBackend.java
  24. 16
      My-Shop/soap-backend/src/services.xml
  25. 22
      WebAPI-Samples/LICENSE
  26. 12
      WebAPI-Samples/README.md
  27. 1
      WebAPI-Samples/SOAP/MTOM/.gitignore
  28. 29
      WebAPI-Samples/SOAP/MTOM/README.md
  29. 14
      WebAPI-Samples/SOAP/MTOM/build.sh
  30. 1
      WebAPI-Samples/SOAP/MTOM/build/.gitignore
  31. 104
      WebAPI-Samples/SOAP/MTOM/doc/README.md
  32. BIN
      WebAPI-Samples/SOAP/MTOM/doc/attach-soapui.png
  33. BIN
      WebAPI-Samples/SOAP/MTOM/doc/axis-services.png
  34. BIN
      WebAPI-Samples/SOAP/MTOM/doc/binding.png
  35. BIN
      WebAPI-Samples/SOAP/MTOM/doc/build.png
  36. BIN
      WebAPI-Samples/SOAP/MTOM/doc/enable-mtom.png
  37. BIN
      WebAPI-Samples/SOAP/MTOM/doc/mtom-soapui.png
  38. BIN
      WebAPI-Samples/SOAP/MTOM/doc/soap-result.png
  39. BIN
      WebAPI-Samples/SOAP/MTOM/doc/soap-soapui.png
  40. 16
      WebAPI-Samples/SOAP/MTOM/src/META-INF/services.xml
  41. 1
      WebAPI-Samples/SOAP/MTOM/src/fr/itix/soapbackend/.gitignore
  42. 16
      WebAPI-Samples/SOAP/MTOM/src/fr/itix/soapbackend/MTOMService.java
  43. 81
      WebAPI-Samples/SOAP/MTOM/wsdl/MtomService.wsdl
  44. 1
      WebAPI-Samples/SOAP/MTOM/wsdl/README.md
  45. 27
      WebAPI-Samples/Utils/ReverseProxy/README.md
  46. 3
      WebAPI-Samples/Utils/ReverseProxy/set-go-path.sh
  47. 96
      WebAPI-Samples/Utils/ReverseProxy/src/itix.fr/forward/main.go

1
My-Shop/README.md

@ -0,0 +1 @@
A Sample Shopping Website (for training purposes)

8
My-Shop/go-backend/set-go-path.sh

@ -0,0 +1,8 @@
# You have to source this file from your shell !
#
# . set-go-path.sh
#
export GOPATH="$PWD"
echo "GOPATH=$GOPATH"

1
My-Shop/go-backend/src/.gitignore

@ -0,0 +1 @@
code.google.com

208
My-Shop/go-backend/src/itix.fr/rest-backend/main.go

@ -0,0 +1,208 @@
package main
import (
"code.google.com/p/gorest"
"net/http"
"fmt"
"strings"
"io/ioutil"
"encoding/json"
)
type Category struct {
Id string
Name string
}
var Categories []Category = []Category {
{ "fringues", "Habillage" },
{ "cuisine", "Cuisine" },
{ "digital", "Digital" },
{ "maison", "Bricolage" } }
type Product struct {
Id int
Name string
Category string
Image string
Description string
Price float32
Stock int
VendorId string
VendorName string
VendorProductId string
IsDigital bool
}
type BuyResponse struct {
ResponseCode string
DownloadUrl string
}
type CallbackResponse struct {
ResponseCode string `json:"code"`
RedirectUrl string `json:"redirect_url"`
}
type BuyCallback struct {
VendorId string
VendorName string
VendorProductId string
}
var Products []Product = []Product {
{ 0, "T-Shirt", "fringues", "brice.jpg", "Le T-Shirt de Brice de Nice.", 99.9, 1, "", "", "", false },
{ 1, "Pull col roulé", "fringues", "pull.jpg", "Un pull à col roulé de couleur marron.", 2.00, 10, "", "", "", false },
{ 2, "Cocotte minute", "cuisine", "cocotte.jpg", "La cocotte minute 'Presto'.", 45.00, 2, "", "", "", false },
{ 3, "Marteau-Piqueur", "maison", "mp.jpg", "Le marteau piqueur 'DESTRUCTOR 2000'.", 600, 5, "", "", "", false } }
// { 4, "Visseuse Ultrasonique", "maison", "visseuse.jpg", "La visseuse-dévisseuse de chez Méga Store.", 600, 5, "mega-store", "Méga Store", "0001", false },
// { 5, "DVD de Harry Poter", "digital", "dvd.jpg", "L'histoire de Ari l'empotteur au pays des merveilles.", 29.9, -1, "zouba-books", "Zouba Books", "12345", true } }
func main() {
gorest.RegisterService(new(MyShopService)) // Register our service
http.Handle("/api/",gorest.Handle())
http.Handle("/", http.FileServer(http.Dir("www-root")))
http.ListenAndServe(":8787", nil)
}
// REST Service Definition
type MyShopService struct {
gorest.RestService `root:"/api/" consumes:"application/json" produces:"application/json"`
getCategories gorest.EndPoint `method:"GET" path:"/shop/category/" output:"[]Category"`
getProductsByCategory gorest.EndPoint `method:"GET" path:"/shop/product/?{category:string}" output:"[]Product"`
searchProducts gorest.EndPoint `method:"GET" path:"/shop/search/{criteria:string}" output:"[]Product"`
getProduct gorest.EndPoint `method:"GET" path:"/shop/product/{id:int}" output:"Product"`
addProduct gorest.EndPoint `method:"POST" path:"/market/product/" postdata:"Product"`
buyProduct gorest.EndPoint `method:"GET" path:"/shop/product/{id:int}/buy" output:"BuyResponse"`
}
func(serv MyShopService) SearchProducts(criteria string) []Product {
fmt.Println(">>> SearchProducts: criteria = ", criteria)
if (criteria == "") {
return Products
}
sliceofcriteria := strings.Split(criteria, " ")
FilteredProducts := []Product {}
for _, p := range Products {
var selected bool = false
name := strings.ToLower(p.Name)
desc := strings.ToLower(p.Description)
for _, c := range sliceofcriteria {
c = strings.ToLower(c)
if strings.Contains(name, c) || strings.Contains(desc, c) {
selected = true
}
}
if selected {
FilteredProducts = append(FilteredProducts, p)
}
}
return FilteredProducts
}
func(serv MyShopService) GetProducts() []Product {
fmt.Println(">>> GetProducts")
return Products
}
func(serv MyShopService) BuyProduct(id int) (resp BuyResponse) {
fmt.Println(">>> BuyProduct: id = ", id)
if id > len(Products) - 1 {
serv.ResponseBuilder().SetResponseCode(404).Overide(true)
return
}
if Products[id].Stock == 0 {
serv.ResponseBuilder().SetResponseCode(409).Overide(true)
return
}
if Products[id].Stock >0 {
Products[id].Stock--
}
return_code := "order-accepted"
redirect_url := ""
if Products[id].VendorId != "" {
elements := []string { "http://api.the-vendor.test:8080/api/vendor", Products[id].VendorId, "callback", Products[id].VendorProductId, "1" }
callback := strings.Join(elements, "/")
fmt.Println(">>> BuyProduct: firing callback to vendor", Products[id].VendorName, "with URL =", callback)
client := &http.Client{}
req, err := http.NewRequest("GET", callback, nil)
resp, err := client.Do(req)
if err != nil {
fmt.Println(">>> BuyProduct: ERROR", err)
return_code = "error"
serv.ResponseBuilder().SetResponseCode(500).Overide(true)
} else {
defer resp.Body.Close()
fmt.Println(">>> BuyProduct: response Status:", resp.Status)
if resp.Status != "200 OK" {
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(">>> BuyProduct: response Body:", string(body))
return_code = "error"
serv.ResponseBuilder().SetResponseCode(500).Overide(true)
} else {
if Products[id].IsDigital {
fmt.Println(">>> BuyProduct: Decoding JSON response")
json_resp := new(CallbackResponse)
json.NewDecoder(resp.Body).Decode(json_resp)
redirect_url = json_resp.RedirectUrl;
} else {
fmt.Println(">>> BuyProduct: Ignoring JSON response")
}
}
}
}
fmt.Println(">>> BuyProduct: return_code =", return_code, "redirect_url =", redirect_url)
resp = BuyResponse { return_code, redirect_url }
return
}
func(serv MyShopService) GetProduct(id int) (p Product) {
fmt.Println(">>> GetProduct: id =", id)
if id > len(Products) - 1 {
serv.ResponseBuilder().SetResponseCode(404).Overide(true)
return
}
p = Products[id]
return
}
func(serv MyShopService) AddProduct(posted Product) {
fmt.Println(">>> AddProduct: posted =", posted)
id := len(Products)
posted.Id = id
Products = append(Products, posted);
serv.ResponseBuilder().Created("/api/shop/product/"+string(id))
}
func(serv MyShopService) GetProductsByCategory(category string) []Product {
fmt.Println(">>> GetProductsByCategory: category =", category)
if (category == "") {
return Products
}
FilteredProducts := []Product {}
for _, p := range Products {
if p.Category == category {
FilteredProducts = append(FilteredProducts, p)
}
}
return FilteredProducts
}
func(serv MyShopService) GetCategories() []Category {
fmt.Println(">>> GetCategories")
return Categories
}

6
My-Shop/go-backend/test/create-product.sh

@ -0,0 +1,6 @@
curl -H "Content-Type: application/json" \
-d '{ "Name": "A New Product", "Category": "maison", "Image": "logo.png", "Description": "My brand new product", "Price": 1, "Stock": 1 }' \
-X POST \
-D - \
http://localhost:8787/api/shop/product/

1
My-Shop/go-backend/www-root/.gitignore

@ -0,0 +1 @@
.DS_Store

BIN
My-Shop/go-backend/www-root/book-1234.pdf

Binary file not shown.

BIN
My-Shop/go-backend/www-root/img/bluray.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
My-Shop/go-backend/www-root/img/brice.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
My-Shop/go-backend/www-root/img/cocotte.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
My-Shop/go-backend/www-root/img/dvd.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
My-Shop/go-backend/www-root/img/livre.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

BIN
My-Shop/go-backend/www-root/img/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
My-Shop/go-backend/www-root/img/mp.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
My-Shop/go-backend/www-root/img/pull.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
My-Shop/go-backend/www-root/img/visseuse.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

31
My-Shop/go-backend/www-root/index.html

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>My Shop</title>
<script type="text/javascript" src="js/dojo/dojo.js"></script>
<script type="text/javascript" src="js/shop.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" href="js/dijit/themes/claro/claro.css" />
</head>
<body class="claro">
<div id="main_pane">
<div id="site_header">
<div id="site_logo"><img src="/img/logo.png" height="75px"/></div>
</div>
<div id="pages_container">
<div style="position: relative;">
<div id="page_title">
<h2><span id="title">Boutique</span></h2>
<h5><span id="subtitle">Subtitle</span></h5>
</div>
<div id="content_pane">
<div id="search_bar">Recherche : <input type="text" id="search_textbox" /><div id="search_results"></div></div>
<div id="category_bar"><div id="category_list_placeholder"></div></div>
<div id="products_pane"></div>
</div>
</div>
</div>
</div>
</body>
</html>

3
My-Shop/go-backend/www-root/js/.gitignore

@ -0,0 +1,3 @@
dojo
dojox
dijit

217
My-Shop/go-backend/www-root/js/shop.js

@ -0,0 +1,217 @@
var apibase = "/api/shop";
var dialog;
require([ "dojo/ready", "dojo/request/xhr", "dojo/dom-construct", "dojo/dom", "dojo/on", "dojo/dom-style", "dojo/dom-attr", "dijit/Dialog" ],
function(ready, xhr, domConstruct, dom, on, domStyle, domAttr, Dialog) {
function setOnCategoryClickHandler(node, catid, catname) {
on(node, "click", function() {
loadProducts(catid);
setSubTitle(catname);
});
}
function setOnProductClickHandler(node, id) {
on(node, "click", function() {
displayProduct(id);
});
}
function setSearchHandler() {
on(dom.byId("search_textbox"), "keyup", doSearch);
on(dom.byId("search_textbox"), "blur", function () {
console.log("SEARCH TEXT BOX >> Blur");
window.setInterval(function () { console.log("SEARCH TEXT BOX >> Je la cache"); domStyle.set("search_results", "visibility", "hidden"); }, 100);
});
on(dom.byId("search_textbox"), "focus", function () {
console.log("SEARCH TEXT BOX >> Focus");
var searchCriteria = domAttr.get("search_textbox", "value");
if (searchCriteria != "") {
domStyle.set("search_results", "visibility", "visible");
}
});
}
function setBuyHandler(node, id) {
on(node, "click", function() {
buyProduct(id);
});
}
function buyProduct(id) {
xhr(apibase + "/product/" + encodeURI(id) + "/buy",
{ handleAs: "json" }
).then(function (data) {
dialog.set("title", "Commande acceptée");
dialog.set("content", "La commande est partie. Vous allez très prochainement recevoir le produit.");
console.log("test...");
if (data.DownloadUrl != null && data.DownloadUrl != "") {
console.log("popup !");
window.location.href = data.DownloadUrl;
}
dialog.show();
displayProduct(id); // Refresh UI
}, function (err) {
if (err.response != null && err.response.status == 409) {
dialog.set("title", "Erreur");
dialog.set("content", "Le produit n'est plus en stock. Désolé.");
dialog.show();
} else {
dialog.set("title", "OOPS");
dialog.set("content", "Erreur interne. Désolé.");
dialog.show();
}
console.log(err);
displayProduct(id); // Refresh UI
}, function (evt) {
});
}
function doSearch(evt) {
var searchCriteria = domAttr.get("search_textbox", "value");
if (searchCriteria == "") {
domStyle.set("search_results", "visibility", "hidden");
return;
} else {
domStyle.set("search_results", "visibility", "visible");
}
xhr(apibase + "/search/" + encodeURI(searchCriteria),
{ handleAs: "json" }
).then(function (data) {
domConstruct.empty("search_results");
var placeholder = dom.byId("search_results");
for (var i = 0; i < data.length; i++) {
var div = domConstruct.create("div", {}, placeholder);
domConstruct.create("img", { width: "32px", src: "/img/" + data[i].Image }, div);
domConstruct.create("span", { textContent: data[i].Name }, div);
setOnProductClickHandler(div, data[i].Id);
}
if (data.length == 0) {
domConstruct.create("span", { textContent: "Aucun résultat", 'class': "no_result" }, placeholder);
}
}, function (err) {
console.log(err);
}, function (evt) {
});
}
function displayProduct(id) {
xhr(apibase + "/product/" + encodeURI(id),
{ handleAs: "json" }
).then(function (data) {
domConstruct.empty("products_pane");
var placeholder = dom.byId("products_pane");
var div = dojo.create("div", { 'class': 'product_detail' }, placeholder);
dojo.create("h1", { 'class': "product_name", textContent: data.Name }, div);
var div2 = dojo.create("div", {}, div)
dojo.create("img", { src: "/img/" + data.Image, height: "200px" }, div2);
dojo.create("span", { 'class': "product_price", textContent: data.Price + " €" }, div2);
if (data.Stock != "-1") {
dojo.create("span", { 'class': "product_stock", textContent: "En Stock: " + data.Stock }, div2);
}
if (data.VendorName != "") {
dojo.create("span", { 'class': "sold_by", textContent: "Vendu par: " + data.VendorName }, div2);
}
var buy_button = dojo.create("div", { 'class': "buy_button" }, div2);
dojo.create("div", { textContent: "Acheter !" }, buy_button);
setBuyHandler(buy_button, data.Id);
dojo.create("div", { 'class': "product_description", textContent: data.Description }, div);
}, function (err) {
if (err.response != null && err.response.status == 404) {
dialog.set("title", "Erreur");
dialog.set("content", "Le produit a été retiré de la vente. Désolé.");
dialog.show();
} else {
dialog.set("title", "OOPS");
dialog.set("content", "Erreur interne. Désolé.");
dialog.show();
}
console.log(err);
}, function (evt) {
});
}
function setSubTitle(name) {
domConstruct.empty("subtitle");
dom.byId("subtitle").textContent = name;
}
function loadProducts(catid) {
var queryString = "";
if (catid != null) {
queryString = "?category=" + encodeURI(catid);
}
xhr(apibase + "/product/" + queryString,
{ handleAs: "json" }
).then(function (data) {
domConstruct.empty("products_pane");
var placeholder = dom.byId("products_pane");
var table = domConstruct.create("table", { 'class': "product_table" }, placeholder);
var current_tr = null;
var i = 0;
for (; i < data.length; i++) {
if (i % 3 == 0) {
current_tr = domConstruct.create("tr", {}, table);
}
var td = domConstruct.create("td", {}, current_tr);
domConstruct.create("img", { width: "100px", src: "/img/" + data[i].Image }, td);
domConstruct.create("span", { textContent: data[i].Name }, td);
setOnProductClickHandler(td, data[i].Id);
}
// Fill remaining columns if less than 3 products
for (; i < 3; i++) {
domConstruct.create("td", {}, current_tr);
}
}, function (err) {
dialog.set("title", "OOPS");
dialog.set("content", "Erreur interne. Désolé.");
dialog.show();
console.log(err);
}, function (evt) {
});
}
function loadCategories() {
xhr(apibase + "/category/",
{ handleAs: "json" }
).then(function (data) {
var placeholder = dom.byId("category_list_placeholder");
domConstruct.empty("category_list_placeholder");
var all_products_node = domConstruct.create("span", { textContent: "Tous les produits", 'class': "category_item" }, placeholder);
setOnCategoryClickHandler(all_products_node, null, "Tous les produits");
for (var i = 0; i < data.length; i++) {
var catid = data[i].Id;
var catname = data[i].Name;
var node = domConstruct.create("span", { textContent: data[i].Name, 'class': "category_item" }, placeholder);
setOnCategoryClickHandler(node, catid, catname);
}
}, function (err) {
dialog.set("title", "OOPS");
dialog.set("content", "Erreur interne. Désolé.");
dialog.show();
console.log(err);
}, function (evt) {
});
}
ready(function() {
loadCategories();
loadProducts(null);
setSubTitle("Tous les produits");
setSearchHandler();
dialog = new Dialog({
id: "global_dialog",
title: "...",
content: "...",
style: "width: 500px; display: none;"
});
});
});

218
My-Shop/go-backend/www-root/style.css

@ -0,0 +1,218 @@
#search_bar {
position: absolute;
top: 0px;
right: 0px;
width: 450px;
height: 50px;
}
#search_textbox {
width: 350px;
height: 20px;
position: absolute;
top: 0px;
right: 0px;
background-color: gray;
}
#search_results {
width: 350px;
position: absolute;
top: 30px;
right: 0px;
background-color: white;
border: 1px solid lightgrey;
z-index: 255;
visibility: hidden;
}
#search_results span, #search_results img {
display:inline-block;
vertical-align:middle;
margin-right: 5px;
}
#search_results div {
cursor: pointer; cursor: hand;
border: 1px solid white;
}
#search_results div:hover {
border: 1px solid gray;
}
.no_result {
color: lightgrey;
font-style: italic;
}
#category_bar {
position: absolute;
top: 100px;
left: 0px;
width: 200px;
}
#products_pane {
position: absolute;
top: 100px;
left: 250px;
right: 0px;
min-height: 300px;
}
#content_pane {
position: absolute;
top: 0px;
left: 30px;
right: 30px;
}
body {
background-color: #AAAAAA;
font-family: Sans-Serif;
}
#main_pane {
position: relative;
width: 80%;
margin: auto;
background-color: #EEEEEE;
min-height: 800px;
}
#site_header {
position: absolute;
top: 0px;
height: 116px;
left: 30px;
right: 30px;
}
#site_logo {
position: absolute;
top: 40px;
width: 345px;
left: 40px;
}
#pages_container {
position: absolute;
top: 140px;
bottom: 30px;
right: 30px;
left: 60px;
}
#page_title {
position: absolute;
top: 10px;
right: 30px;
left: 30px;
font: normal normal normal 25px/1.2em sans-serif;
}
#page_title > h2 {
line-height: 1.1em;
font: normal normal normal 22px/1.1em sans-serif;
color: #00CCFF;
margin : 0;
padding : 0;
border : 0;
outline : 0;
}
#page_title > h5 {
color: #7F7F7F;
line-height: 1.2em;
letter-spacing: normal;
margin : 0;
padding : 0;
border : 0;
outline : 0;
}
.product_table {
width: 100%;
border-spacing: 10px;
border-collapse: separate;
}
.product_table span, .product_table img {
display: block;
margin: auto;
}
.product_table img {
margin-bottom: 10px;
}
.product_table td {
padding-top: 10px;
padding-bottom: 10px;
text-align: center;
background-color: white;
width: 33%;
color: darkgrey;
cursor: pointer; cursor: hand;
}
.category_item {
display: block;
margin-bottom: 10px;
text-decoration: none;
cursor: pointer; cursor: hand;
}
.category_item:hover {
text-decoration: underline;
}
.product_price {
position: absolute;
top: 20px;
left: 300px;
color: red;
font-size: x-large;
}
.product_detail > div {
position: relative;
}
.product_stock {
position: absolute;
top: 60px;
left: 300px;
color: darkgreen;
}
.sold_by {
position: absolute;
top: 80px;
left: 300px;
color: darkgreen;
}
.product_description {
margin-top: 20px;
color: darkgrey;
}
.buy_button {
position: absolute;
top: 150px;
left: 300px;
background-color: red;
color: white;
width: 100px;
height: 30px;
cursor: pointer; cursor: hand;
}
.buy_button > div {
text-align: center;
line-height: 30px;
vertical-align: middle;
}

13
My-Shop/soap-backend/deploy.sh

@ -0,0 +1,13 @@
#!/bin/sh
(cd src && javac fr/itix/soapbackend/*.java)
BASE=tomcat/webapps/axis2/WEB-INF/services/
NAME=VendorBackend
mkdir -p build/$NAME/META-INF build/$NAME/fr/itix/soapbackend/
cp src/fr/itix/soapbackend/*.class build/$NAME/fr/itix/soapbackend/
cp src/services.xml build/$NAME/META-INF/
cp -rv build/$NAME $BASE/

1
My-Shop/soap-backend/src/fr/itix/soapbackend/.gitignore

@ -0,0 +1 @@
*.class

14
My-Shop/soap-backend/src/fr/itix/soapbackend/VendorBackend.java

@ -0,0 +1,14 @@
package fr.itix.soapbackend;
public class VendorBackend {
public String NotifySale(String productId, int number, String callerID) {
if ("12345".equals(productId) && "zouba-books".equals(callerID)) {
return "OK;http://online-shop.zouba-books.test:8787/book-1234.pdf";
}
if ("0001".equals(productId) && "mega-store".equals(callerID)) {
return "OK;";
}
throw new RuntimeException("Unknown caller id or wrong product id");
}
}

16
My-Shop/soap-backend/src/services.xml

@ -0,0 +1,16 @@
<service name="VendorBackend" scope="application">
<description>
Vendor Backend
</description>
<messageReceivers>
<messageReceiver
mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
<messageReceiver
mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceClass">
fr.itix.soapbackend.VendorBackend
</parameter>
</service>

22
WebAPI-Samples/LICENSE

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Nicolas MASSE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

12
WebAPI-Samples/README.md

@ -0,0 +1,12 @@
## WebAPI-Samples
Sample Code for various Web API Protocols (SOAP, REST, etc.)
### SOAP samples
- [MTOM Attachments in Java](./SOAP/MTOM/)
### REST samples
- TODO
### Utils
- [A Reverse Proxy written in GO](./Utils/ReverseProxy/)

1
WebAPI-Samples/SOAP/MTOM/.gitignore

@ -0,0 +1 @@
tomcat

29
WebAPI-Samples/SOAP/MTOM/README.md

@ -0,0 +1,29 @@
# MTOM Sample Server Code
## Introduction to MTOM
MTOM is a standard to attach a file to a SOAP message (others way to do so are: MIME Attachments or in-line base64).
For a good introduction to MTOM read :
- http://www.mkyong.com/webservices/jax-ws/jax-ws-attachment-with-mtom/
- https://axis.apache.org/axis2/java/core/docs/mtom-guide.html
- http://stackoverflow.com/questions/215741/how-does-mtom-work
### How to know when a SOAP Message use MTOM ?
The easy way :
- The HTTP request is a mime multipart
```
POST /path/to/ws HTTP/1.1
Content-type: multipart/related; start="bla"; type="application/xop+xml"; boundary="uuid:bla...";
```
- The SOAP Payload contains `Include` elements in the namespace `http://www.w3.org/2004/08/xop/include`
```
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="bla bla bla">
```
## MTOM Server Code Setup
See [the documentation](./doc/README.md).

14
WebAPI-Samples/SOAP/MTOM/build.sh

@ -0,0 +1,14 @@
#!/bin/bash
NAME=SoapBackend
function die() {
echo "ERROR: $@"
exit 1
}
cd src || die "No source code"
javac $(find . -iname *.java) || die "Compilation error"
jar cvf ../build/$NAME.aar $(find . -iname *.class -or -iname *.xml) || die "Cannot build jar"

1
WebAPI-Samples/SOAP/MTOM/build/.gitignore

@ -0,0 +1 @@
*.aar

104
WebAPI-Samples/SOAP/MTOM/doc/README.md

@ -0,0 +1,104 @@
## MTOM Server Code Setup
### Pre-requisites
To make this sample code work, you need:
- Tomcat (tested against version 7)
- Axis2 (tested against version 1.6)
Get tomcat7 and install it in a `tomcat` folder.
```
$ wget http://www.eu.apache.org/dist/tomcat/tomcat-7/v7.0.62/bin/apache-tomcat-7.0.62.tar.gz && tar zxvf apache-tomcat-*.tar.gz && mv apache-tomcat-*/ tomcat
```
Get axis2 and install the axis2.jar in `tomcat/webapps/axis2`
```
$ cd tomcat/webapps
$ wget http://www.eu.apache.org/dist//axis/axis2/java/core/1.6.2/axis2-1.6.2-war.zip && unzip axis2-1.6.2-war.zip axis2.war
$ cd ..
$ ./bin/startup.sh
```
### Build
Build the .aar archive and deploy it.
![build the .aar archive](./build.png)
### Change the Axis Configuration to enable MTOM
Edit the Axis2 configuration file: `tomcat/webapps/axis2/WEB-INF/conf/axis2.xml` and set the `enableMTOM` parameter to `true`.
![set "enableMTOM" to "true"](./enable-mtom.png)
**DO NOT FORGET TO RESTART TOMCAT !!!**
## Test the MTOM attachment
### Retrieve the WSDL
Make sure Tomcat is started and the .aar archive is deployed. Then go to http://localhost:8080/axis2/services/listServices.
Click on the `MTOMService` to get the WSDL.
*Note:* make sure you save the file in its original format (File > Save As...). **Do not use copy paste that may break the XML format.**
Just in case, you can find a copy of the WSDL [here](../wsdl/MtomService.wsdl).
### Test in SOAP UI
1. Get SOAP UI (tested with version 5)
2. Import the WSDL
3. Open the auto-generated request for the `MtomServiceSoap12Binding` binding
![](./binding.png)
4. Make sure `Enable MTOM` and `Force MTOM` in the bottom left pane are set to `true`.
![](./mtom-soapui.png)
5. Import your attachment in the bottom pane.
![](./attach-soapui.png)
6. Make sure to reference the attachment in your SOAP message using `cid:attachment-name`.
![](./soap-soapui.png)
7. Make sure the attachement part in the bottom pane is set and the attachment type is set to `XOP`
![](./attach-soapui.png)
8. Fire the request and observe the result !
![](./soap-result.png)
Note: by clicking on the raw button in the request pane, you can have a look at the complete HTTP request.
```
POST http://localhost:8080/axis2/services/MtomService.MtomServiceHttpSoap12Endpoint/ HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: multipart/related; type="application/xop+xml"; start="<rootpart@soapui.org>"; start-info="application/soap+xml"; action="urn:countBytes"; boundary="----=_Part_1_223849750.1433937520698"
MIME-Version: 1.0
Content-Length: 912
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
------=_Part_1_223849750.1433937520698
Content-Type: application/xop+xml; charset=UTF-8; type="application/soap+xml"; action="countBytes"
Content-Transfer-Encoding: 8bit
Content-ID: <rootpart@soapui.org>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:mtom="http://itix.fr/soap/mtom">
<soap:Header/>
<soap:Body>
<mtom:countBytes>
<!--Optional:-->
<mtom:args0><inc:Include href="cid:my-attachment.txt" xmlns:inc="http://www.w3.org/2004/08/xop/include"/></mtom:args0>
</mtom:countBytes>
</soap:Body>
</soap:Envelope>
------=_Part_1_223849750.1433937520698
Content-Type: text/plain; charset=us-ascii; name=my-attachment.txt
Content-Transfer-Encoding: 7bit
Content-ID: <my-attachment.txt>
Content-Disposition: attachment; name="my-attachment.txt"; filename="my-attachment.txt"
Hello World !
------=_Part_1_223849750.1433937520698--
```

BIN
WebAPI-Samples/SOAP/MTOM/doc/attach-soapui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/axis-services.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/binding.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/build.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/enable-mtom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/mtom-soapui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/soap-result.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
WebAPI-Samples/SOAP/MTOM/doc/soap-soapui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

16
WebAPI-Samples/SOAP/MTOM/src/META-INF/services.xml

@ -0,0 +1,16 @@
<service name="VendorBackend" scope="application">
<description>
SOAP Backend that exposes an MTOM enabled service
</description>
<messageReceivers>
<messageReceiver
mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
<messageReceiver
mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceClass">
fr.itix.soapbackend.MTOMService
</parameter>
</service>

1
WebAPI-Samples/SOAP/MTOM/src/fr/itix/soapbackend/.gitignore

@ -0,0 +1 @@
*.class

16
WebAPI-Samples/SOAP/MTOM/src/fr/itix/soapbackend/MTOMService.java

@ -0,0 +1,16 @@
package fr.itix.soapbackend;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.soap.MTOM;
@MTOM
@WebService(name="MtomPortType",
serviceName="MtomService",
targetNamespace="http://itix.fr/soap/mtom")
public class MTOMService {
@WebMethod
public int countBytes(byte[] bytes) {
return bytes.length;
}
}

81
WebAPI-Samples/SOAP/MTOM/wsdl/MtomService.wsdl

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ns="http://itix.fr/soap/mtom" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:ns1="http://org.apache.axis2/xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://itix.fr/soap/mtom">
<wsdl:documentation>VendorBackend</wsdl:documentation>
<wsdl:types>
<xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://itix.fr/soap/mtom">
<xs:element name="countBytes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="args0" nillable="true" type="xs:base64Binary"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="countBytesResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="countBytesRequest">
<wsdl:part name="parameters" element="ns:countBytes"/>
</wsdl:message>
<wsdl:message name="countBytesResponse">
<wsdl:part name="parameters" element="ns:countBytesResponse"/>
</wsdl:message>
<wsdl:portType name="MtomServicePortType">
<wsdl:operation name="countBytes">
<wsdl:input message="ns:countBytesRequest" wsaw:Action="urn:countBytes"/>
<wsdl:output message="ns:countBytesResponse" wsaw:Action="urn:countBytesResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="MtomServiceSoap11Binding" type="ns:MtomServicePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="countBytes">
<soap:operation soapAction="urn:countBytes" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="MtomServiceSoap12Binding" type="ns:MtomServicePortType">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="countBytes">
<soap12:operation soapAction="urn:countBytes" style="document"/>
<wsdl:input>
<soap12:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap12:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="MtomServiceHttpBinding" type="ns:MtomServicePortType">
<http:binding verb="POST"/>
<wsdl:operation name="countBytes">
<http:operation location="countBytes"/>
<wsdl:input>
<mime:content type="application/xml" part="parameters"/>
</wsdl:input>
<wsdl:output>
<mime:content type="application/xml" part="parameters"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="MtomService">
<wsdl:port name="MtomServiceHttpSoap11Endpoint" binding="ns:MtomServiceSoap11Binding">
<soap:address location="http://localhost:8080/axis2/services/MtomService.MtomServiceHttpSoap11Endpoint/"/>
</wsdl:port>
<wsdl:port name="MtomServiceHttpSoap12Endpoint" binding="ns:MtomServiceSoap12Binding">
<soap12:address location="http://localhost:8080/axis2/services/MtomService.MtomServiceHttpSoap12Endpoint/"/>
</wsdl:port>
<wsdl:port name="MtomServiceHttpEndpoint" binding="ns:MtomServiceHttpBinding">
<http:address location="http://localhost:8080/axis2/services/MtomService.MtomServiceHttpEndpoint/"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

1
WebAPI-Samples/SOAP/MTOM/wsdl/README.md

@ -0,0 +1 @@
This a sample WSDL, generated by Axis

27
WebAPI-Samples/Utils/ReverseProxy/README.md

@ -0,0 +1,27 @@
## Reverse Proxy written in GO
This reverse proxy listens on a local port and forward all requests to a named host. It honors the proxy environment variables.
### Initial Need
Once upon a time, I had to circumvent a bug in a product that could not handle correctly an HTTPS connection to a proxy.
Since it was an HTTPS connection, I could not setup a transparent proxy.
### What it does
It opens a local port and listen to HTTP requests, forwards the requests to a named host and send back the response.
### How to use it
```bash
go run src/itix.fr/forward/main.go -local-port 8080 -target https://www.opentrust.com
curl -D - http://localhost:8080/robots.txt
```
If you want to go through a proxy, do not forget to set the ```http_proxy``` and ```https_proxy``` variables !
```bash
export http_proxy=http://my.proxy:8888/
export https_proxy=http://my.proxy:8888/
```

3
WebAPI-Samples/Utils/ReverseProxy/set-go-path.sh

@ -0,0 +1,3 @@
export GOPATH="$PWD"
echo "GOPATH=$GOPATH"

96
WebAPI-Samples/Utils/ReverseProxy/src/itix.fr/forward/main.go

@ -0,0 +1,96 @@
package main
import (
"os"
"fmt"
"flag"
"net/http"
"net/http/httputil"
"net/url"
)
// The local port on which we should listen to
var local_port int
// The target URL on which we should redirect requests
var target string
func init() {
// --local-port=9009
flag.IntVar(&local_port, "local-port", 9090, "the TCP port to listen to")
// --target=http://www.perdu.com
flag.StringVar(&target, "target", "http://www.perdu.com", "the target URL to redirect the request to")
}
// The MyResponseWriter is a wrapper around the standard http.ResponseWriter
// We need it to retrieve the http status code of an http response
type MyResponseWriter struct {
Underlying http.ResponseWriter
Status int
}
func (mrw *MyResponseWriter) Header() http.Header {
return mrw.Underlying.Header()
}
func (mrw *MyResponseWriter) Write(b []byte) (int, error) {
return mrw.Underlying.Write(b)
}
func (mrw *MyResponseWriter) WriteHeader(s int) {
mrw.Status = s
mrw.Underlying.WriteHeader(s)
}
func main() {
// Parse the command line arguments
flag.Parse()
// Parse the target URL and perform some sanity checks
url, err := url.Parse(target)
if err != nil {
panic(err)
}
// Initialize a Reverse Proxy object with a custom director
proxy := httputil.NewSingleHostReverseProxy(url)
underlying_director := proxy.Director
proxy.Director = func(req *http.Request) {
// Let the underlying director do the mandatory job
underlying_director(req)
// Custom Handling
// ---------------
//
// Filter out the "Host" header sent by the client
// otherwise the target server won't be able to find the
// matching virtual host. The correct host header will be
// added automatically by the net/http package.
req.Host = ""
}
http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
// Log the incoming request (including headers)
fmt.Printf("%v %v HTTP/1.1\n", req.Method, req.URL)
req.Header.Write(os.Stdout)
fmt.Println()
// Wrap the standard response writer with our own
// implementation because we need the status code of the
// response and that field is not exported by default
mrw := &MyResponseWriter{ Underlying: rw }
// Let the reverse proxy handle the request
proxy.ServeHTTP(mrw, req)
// Log the response
fmt.Printf("%v %v\n", mrw.Status, http.StatusText(mrw.Status))
mrw.Header().Write(os.Stdout)
fmt.Println()
})
fmt.Printf("Listening on port %v for incoming requests...\n", local_port)
http.ListenAndServe(fmt.Sprintf(":%v", local_port), nil)
}
Loading…
Cancel
Save