diff --git a/README.md b/README.md index 23d6e2a..79ad6b5 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ The following sample applications demonstrate the capabilities of [Spring Web Services]. See the README within each sample project for more information and additional instructions. +- [airline](./airline) - a complete airline sample that shows both Web Service and + O/X Mapping functionality in a complete application - [echo](./echo) - a simple sample that shows a bare-bones Echo service - [mtom](./mtom) - shows how to use MTOM and JAXB2 marshalling - [stockquote](./stockquote) - shows how to use WS-Addressing and the Java 6 HTTP Server diff --git a/airline/README.md b/airline/README.md new file mode 100644 index 0000000..09cf345 --- /dev/null +++ b/airline/README.md @@ -0,0 +1,18 @@ +# Airline Sample + +Features a web service on top of an airline reservation system, backed by a +database. The web service works by using XML Marshalling techniques (JAXB2), +and XPath in combination with XPath queries to pull information from a message. +Additionally, the Airline service has JMS support. All messages follow the +messages.xsd schema in src/main/webapp. + +## Build and deploy + +See the main [README](../README.md) for build instructions. + +## License + +[Spring Web Services] is released under version 2.0 of the [Apache License]. + +[Spring Web Services]: http://projects.spring.io/spring-ws +[Apache License]: http://www.apache.org/licenses/LICENSE-2.0 \ No newline at end of file diff --git a/airline/build.gradle b/airline/build.gradle new file mode 100644 index 0000000..5736641 --- /dev/null +++ b/airline/build.gradle @@ -0,0 +1,19 @@ +subprojects { + apply plugin: 'java' + apply plugin: 'eclipse' + apply plugin: 'idea' + + repositories { + maven { url 'http://repo.spring.io/libs-release' } + } + + dependencies { + testCompile("junit:junit:4.10") + testCompile("org.easymock:easymock:3.1") + } + +} + +task wrapper(type: Wrapper) { + gradleVersion = '1.8' +} diff --git a/airline/client/airline.wsdl b/airline/client/airline.wsdl new file mode 100644 index 0000000..73cc5a5 --- /dev/null +++ b/airline/client/airline.wsdl @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/airline/client/axis1/build.gradle b/airline/client/axis1/build.gradle new file mode 100644 index 0000000..60715cc --- /dev/null +++ b/airline/client/axis1/build.gradle @@ -0,0 +1,62 @@ +configurations { + axis +} + +ext.axisVersion = '1.4' + +task axis { + ext.sourcesDir = "${buildDir}/generated-sources/axis" + ext.classesDir = "${buildDir}/classes/axis" + ext.wsdl = "${projectDir}/../airline.wsdl" + + inputs.files wsdl + outputs.dir classesDir + + doLast() { + project.ant { + taskdef name: "axis", classname: "org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask", + classpath: configurations.axis.asPath + + mkdir(dir: sourcesDir) + mkdir(dir: classesDir) + + axis(output: sourcesDir, url: wsdl) { + mapping(namespace: "http://www.springframework.org/spring-ws/samples/airline/definitions", + package: "org.springframework.ws.samples.airline.client.axis1") + mapping(namespace: "http://www.springframework.org/spring-ws/samples/airline/schemas/messages", + package: "org.springframework.ws.samples.airline.client.axis1") + mapping(namespace: "http://www.springframework.org/spring-ws/samples/airline/schemas/types", + package: "org.springframework.ws.samples.airline.client.axis1") + } + + javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true, + debugLevel: "lines,vars,source", + classpath: configurations.axis.asPath) { + src(path: sourcesDir) + include(name: "**/*.java") + include(name: "*.java") + } + + copy(todir: classesDir) { + fileset(dir: sourcesDir, erroronmissingdir: false) { + exclude(name: "**/*.java") + } + } + + } + } +} + +dependencies { + compile("axis:axis:$axisVersion") + compile("axis:axis-jaxrpc:$axisVersion") + compile(files(axis.classesDir).builtBy(axis)) + runtime("log4j:log4j:1.2.16") + axis("axis:axis:$axisVersion") + axis("axis:axis-ant:$axisVersion") +} + +task runClient(dependsOn: 'classes', type:JavaExec) { + main = "org.springframework.ws.samples.airline.client.axis1.Main" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/airline/client/axis1/readme.txt b/airline/client/axis1/readme.txt new file mode 100644 index 0000000..c9fa309 --- /dev/null +++ b/airline/client/axis1/readme.txt @@ -0,0 +1,10 @@ +SPRING WEB SERVICES + +This directory contains a Java client for the Airline Web Service that uses JAX-RPC. +The client can be run from the provided ant file, by calling "ant run". + +SAJA Client Sample table of contents +--------------------------------------------------- +* src - The source files for the client +* build.xml - Ant build file with a 'build' and a 'run' target + diff --git a/airline/client/axis1/src/main/java/org/springframework/ws/samples/airline/client/axis1/Main.java b/airline/client/axis1/src/main/java/org/springframework/ws/samples/airline/client/axis1/Main.java new file mode 100644 index 0000000..18c25b4 --- /dev/null +++ b/airline/client/axis1/src/main/java/org/springframework/ws/samples/airline/client/axis1/Main.java @@ -0,0 +1,94 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.axis1; + +import java.rmi.RemoteException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Calendar; +import javax.xml.rpc.ServiceException; + +/** + * Simple client that calls the GetFlights and BookFlight operations using JAX-RPC (Axis 1). + * + * @author Arjen Poutsma + */ +public class Main { + + public static void main(String[] args) throws ServiceException, RemoteException { + AirlineServiceLocator service = new AirlineServiceLocator(); + if (args.length > 0) { + service.setAirlineSoap11EndpointAddress(args[0]); + } + Airline airline = service.getAirlineSoap11(); + GetFlightsRequest request = new GetFlightsRequest(); + request.setFrom("AMS"); + request.setTo("VCE"); + Calendar departureCalendar = Calendar.getInstance(); + departureCalendar.set(Calendar.YEAR, 2006); + departureCalendar.set(Calendar.MONTH, Calendar.JANUARY); + departureCalendar.set(Calendar.DATE, 31); + Date departureDate = departureCalendar.getTime(); + request.setDepartureDate(departureDate); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + System.out.println("Requesting flights on " + dateFormat.format(departureDate)); + Flight[] flights = airline.getFlights(request); + System.out.println("Got " + flights.length + " results"); + if (flights.length > 0) + { + // Book the first flight using John Doe as a frequent flyer + BookFlightRequest bookFlightRequest = new BookFlightRequest(); + bookFlightRequest.setFlightNumber(flights[0].getNumber()); + bookFlightRequest.setDepartureTime(flights[0].getDepartureTime()); + BookFlightRequestPassengers passengers = new BookFlightRequestPassengers(); + passengers.setUsername("john"); + bookFlightRequest.setPassengers(passengers); + Ticket ticket = airline.bookFlight(bookFlightRequest); + writeTicket(ticket); + } + } + + private static void writeTicket(Ticket ticket) { + System.out.println("Ticket " + ticket.getId()); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + System.out.println("Ticket issue date:\t" + dateFormat.format(ticket.getIssueDate())); + for (int i = 0; i < ticket.getPassengers().length; i++) { + writeName(ticket.getPassengers()[i]); + + } + writeFlight(ticket.getFlight()); + } + + private static void writeName(Name name) { + System.out.println("Passenger Name:"); + System.out.println(name.getFirst() + " " + name.getLast()); + System.out.println("------------"); + } + + private static void writeFlight(Flight flight) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + System.out.println(dateFormat.format(flight.getDepartureTime().getTime())); + System.out.println(flight.getNumber() + "\t" + flight.getServiceClass()); + System.out.println("------------"); + System.out.println("Depart:\t" + flight.getFrom().getCode() + "-" + flight.getFrom().getName() + "\t" + dateFormat.format(flight.getDepartureTime().getTime())); + System.out.println("\t" + flight.getFrom().getCity()); + System.out.println("Arrive:\t" + flight.getTo().getCode() + "-" + flight.getTo().getName() + "\t" + dateFormat.format(flight.getArrivalTime().getTime())); + System.out.println("\t" + flight.getTo().getCity()); + } + + +} diff --git a/airline/client/axis1/src/main/resources/log4j.properties b/airline/client/axis1/src/main/resources/log4j.properties new file mode 100644 index 0000000..ef74297 --- /dev/null +++ b/airline/client/axis1/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=WARN, stdout +log4j.logger.org.springframework.ws=DEBUG + + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n \ No newline at end of file diff --git a/airline/client/jax-ws/build.gradle b/airline/client/jax-ws/build.gradle new file mode 100644 index 0000000..5179794 --- /dev/null +++ b/airline/client/jax-ws/build.gradle @@ -0,0 +1,38 @@ +configurations { + wsimport +} + +ext.springWsVersion = '2.1.4.RELEASE' + +task wsImport { + ext.outputDir = "${buildDir}/classes/wsimport" + ext.wsdl = "${projectDir}/../airline.wsdl" + + inputs.files wsdl + outputs.dir outputDir + + doLast() { + project.ant { + taskdef name: "wsimport", classname: "com.sun.tools.ws.ant.WsImport", + classpath: configurations.wsimport.asPath + mkdir(dir: outputDir) + + wsimport(destdir: outputDir, wsdl: wsdl, + package: "org.springframework.ws.samples.airline.client.jaxws") { + produces(dir: outputDir, includes: "**/*.class") + } + } + } +} + +dependencies { + compile("org.springframework.ws:spring-ws-core:$springWsVersion") + compile(files(wsImport.outputDir).builtBy(wsImport)) + runtime("log4j:log4j:1.2.16") + wsimport "com.sun.xml.ws:jaxws-tools:2.1.7" +} + +task runClient(dependsOn: 'classes', type:JavaExec) { + main = "org.springframework.ws.samples.airline.client.jaxws.Main" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/airline/client/jax-ws/readme.txt b/airline/client/jax-ws/readme.txt new file mode 100644 index 0000000..89a04c4 --- /dev/null +++ b/airline/client/jax-ws/readme.txt @@ -0,0 +1,11 @@ +SPRING WEB SERVICES + +This directory contains a Java client for the Airline Web Service that uses JAX-WS. +The client can be run from the provided ant file, by calling "ant run". +Note that the airline sample has to be running before invoking this target. + +Client Sample table of contents +--------------------------------------------------- +* src - The source files for the client +* build.xml - Ant build file with a 'build' and a 'run' target + diff --git a/airline/client/jax-ws/src/main/java/org/springframework/ws/samples/airline/client/jaxws/Main.java b/airline/client/jax-ws/src/main/java/org/springframework/ws/samples/airline/client/jaxws/Main.java new file mode 100644 index 0000000..12e2d2b --- /dev/null +++ b/airline/client/jax-ws/src/main/java/org/springframework/ws/samples/airline/client/jaxws/Main.java @@ -0,0 +1,104 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.jaxws; + +import java.net.MalformedURLException; +import java.net.URL; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; +import javax.xml.ws.soap.SOAPFaultException; + +/** + * Simple client that calls the GetFlights and BookFlight operations using JAX-WS. + * + * @author Arjen Poutsma + */ +public class Main { + + public static void main(String[] args) throws MalformedURLException, DatatypeConfigurationException { + try { + AirlineService service; + if (args.length == 0) { + service = new AirlineService(); + } + else { + QName serviceName = new QName("http://www.springframework.org/spring-ws/samples/airline/definitions", + "AirlineService"); + service = new AirlineService(new URL(args[0]), serviceName); + } + Airline airline = service.getAirlineSoap11(); + GetFlightsRequest request = new GetFlightsRequest(); + request.setFrom("AMS"); + request.setTo("VCE"); + XMLGregorianCalendar departureDate = + DatatypeFactory.newInstance().newXMLGregorianCalendarDate(2006, 1, 31, + DatatypeConstants.FIELD_UNDEFINED); + request.setDepartureDate(departureDate); + System.out.format("Requesting flights on %1tD%n", departureDate.toGregorianCalendar()); + GetFlightsResponse response = airline.getFlights(request); + System.out.format("Got %1d results%n", response.getFlight().size()); + if (!response.getFlight().isEmpty()) + // Book the first flight using John Doe as a frequent flyer + { + Flight flight = response.getFlight().get(0); + BookFlightRequest bookFlightRequest = new BookFlightRequest(); + bookFlightRequest.setFlightNumber(flight.getNumber()); + bookFlightRequest.setDepartureTime(flight.getDepartureTime()); + BookFlightRequest.Passengers passengers = new BookFlightRequest.Passengers(); + passengers.getPassengerOrUsername().add("john"); + bookFlightRequest.setPassengers(passengers); + Ticket ticket = airline.bookFlight(bookFlightRequest); + writeTicket(ticket); + } + } + catch (SOAPFaultException ex) { + System.out.format("SOAP Fault Code %1s%n", ex.getFault().getFaultCodeAsQName()); + System.out.format("SOAP Fault String: %1s%n", ex.getFault().getFaultString()); + + } + } + + private static void writeTicket(Ticket ticket) { + System.out.format("Ticket %1d%n", ticket.getId()); + System.out.format("Ticket issue date:\t%1tD%n", ticket.getIssueDate().toGregorianCalendar()); + for (Name passenger : ticket.getPassengers().getPassenger()) { + writeName(passenger); + + } + writeFlight(ticket.flight); + } + + private static void writeName(Name name) { + System.out.format("Passenger Name:%n"); + System.out.format("%1s %2s%n", name.getFirst(), name.getLast()); + System.out.format("------------%n"); + } + + private static void writeFlight(Flight flight) { + System.out.format("%1tD%n", flight.getDepartureTime().toGregorianCalendar()); + System.out.format("%1s\t%2s%n", flight.getNumber(), flight.getServiceClass()); + System.out.format("------------%n"); + System.out.format("Depart:\t%1s-%2s\t%tR%n", flight.getFrom().getCode(), flight.getFrom().getName(), flight.getDepartureTime().toGregorianCalendar()); + System.out.format("\t%1s%n", flight.getFrom().getCity()); + System.out.format("Arrive:\t%1s-%2s\t%tR%n", flight.getTo().getCode(), flight.getTo().getName(), flight.getArrivalTime().toGregorianCalendar()); + System.out.format("\t%1s%n", flight.getTo().getCity()); + } + +} diff --git a/airline/client/jms/build.gradle b/airline/client/jms/build.gradle new file mode 100644 index 0000000..e2cf3a8 --- /dev/null +++ b/airline/client/jms/build.gradle @@ -0,0 +1,14 @@ +ext.springWsVersion = '2.1.4.RELEASE' + +dependencies { + compile("org.springframework.ws:spring-ws-core:$springWsVersion") + compile("org.springframework.ws:spring-ws-support:$springWsVersion") + compile("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1") + runtime("log4j:log4j:1.2.16") + runtime("org.apache.activemq:activemq-core:4.1.2") +} + +task runClient(dependsOn: 'classes', type:JavaExec) { + main = "org.springframework.ws.samples.airline.client.jms.JmsClient" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/airline/client/jms/readme.txt b/airline/client/jms/readme.txt new file mode 100644 index 0000000..36aa269 --- /dev/null +++ b/airline/client/jms/readme.txt @@ -0,0 +1,10 @@ +SPRING WEB SERVICES + +This directory contains a client for the Airline Web Service that uses JMS: Java Message Service. The client can be run +from the provided ant file, by calling "ant run". + +JMS Client Sample table of contents +--------------------------------------------------- +* src - The source files for the client +* build.xml - Ant build file with a 'build' and a 'run' target + diff --git a/airline/client/jms/src/main/java/org/springframework/ws/samples/airline/client/jms/JmsClient.java b/airline/client/jms/src/main/java/org/springframework/ws/samples/airline/client/jms/JmsClient.java new file mode 100644 index 0000000..34c5f68 --- /dev/null +++ b/airline/client/jms/src/main/java/org/springframework/ws/samples/airline/client/jms/JmsClient.java @@ -0,0 +1,48 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.jms; + +import java.io.IOException; +import javax.jms.JMSException; +import javax.xml.soap.SOAPException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.stream.StreamResult; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.ws.client.core.support.WebServiceGatewaySupport; +import org.springframework.xml.transform.StringSource; + +/** @author Arjen Poutsma */ +public class JmsClient extends WebServiceGatewaySupport { + + private static final String PAYLOAD = + "" + + "AMS" + "VCE" + + "2006-01-31" + ""; + + public void getFlights() throws SOAPException, IOException, TransformerException, JMSException { + getWebServiceTemplate().sendSourceAndReceiveToResult(new StringSource(PAYLOAD), new StreamResult(System.out)); + } + + public static void main(String[] args) throws Exception { + ApplicationContext applicationContext = + new ClassPathXmlApplicationContext("applicationContext.xml", JmsClient.class); + JmsClient jmsClient = (JmsClient) applicationContext.getBean("jmsClient", JmsClient.class); + jmsClient.getFlights(); + } +} \ No newline at end of file diff --git a/airline/client/jms/src/main/resources/log4j.properties b/airline/client/jms/src/main/resources/log4j.properties new file mode 100644 index 0000000..ef74297 --- /dev/null +++ b/airline/client/jms/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=WARN, stdout +log4j.logger.org.springframework.ws=DEBUG + + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n \ No newline at end of file diff --git a/airline/client/jms/src/main/resources/org/springframework/ws/samples/airline/client/jms/applicationContext.xml b/airline/client/jms/src/main/resources/org/springframework/ws/samples/airline/client/jms/applicationContext.xml new file mode 100644 index 0000000..790904c --- /dev/null +++ b/airline/client/jms/src/main/resources/org/springframework/ws/samples/airline/client/jms/applicationContext.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/airline/client/saaj/build.gradle b/airline/client/saaj/build.gradle new file mode 100644 index 0000000..ef6d75d --- /dev/null +++ b/airline/client/saaj/build.gradle @@ -0,0 +1,10 @@ +dependencies { + compile("com.sun.xml.wss:xws-security:3.0") { + exclude group: 'javax.xml.crypto', module: 'xmldsig' + } +} + +task runClient(dependsOn: 'classes', type:JavaExec) { + main = "org.springframework.ws.samples.airline.client.saaj.Driver" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/airline/client/saaj/readme.txt b/airline/client/saaj/readme.txt new file mode 100644 index 0000000..b65bd62 --- /dev/null +++ b/airline/client/saaj/readme.txt @@ -0,0 +1,11 @@ +SPRING WEB SERVICES + +This directory contains two Java clients for the Airline Web Service that use SAAJ: SOAP with Attachments API for Java. +There is a client for the GetFlights operation and a client for the secure GetFrequentFlyerMileage operation. Both +clients can be run from the provided ant file, by calling "ant run". + +SAJA Client Sample table of contents +--------------------------------------------------- +* src - The source files for the client +* build.xml - Ant build file with a 'build' and a 'run' target + diff --git a/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/Driver.java b/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/Driver.java new file mode 100644 index 0000000..dc5459b --- /dev/null +++ b/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/Driver.java @@ -0,0 +1,38 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.saaj; + +/** + * @author Arjen Poutsma + */ +public class Driver { + + public static void main(String[] args) throws Exception { + String url = "http://localhost:8080/airline-server/services"; + if (args.length > 0) { + url = args[0]; + } + GetFlights getFlights = new GetFlights(url); + getFlights.getFlights(); + + String username = "john"; + String password = "changeme"; + GetFrequentFlyerMileage getMileage = new GetFrequentFlyerMileage(url); + getMileage.getMileage(username, password); + } + +} diff --git a/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/GetFlights.java b/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/GetFlights.java new file mode 100644 index 0000000..b6bf6f3 --- /dev/null +++ b/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/GetFlights.java @@ -0,0 +1,117 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.saaj; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Iterator; +import javax.xml.soap.MessageFactory; +import javax.xml.soap.Name; +import javax.xml.soap.SOAPBodyElement; +import javax.xml.soap.SOAPConnection; +import javax.xml.soap.SOAPConnectionFactory; +import javax.xml.soap.SOAPElement; +import javax.xml.soap.SOAPEnvelope; +import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPFault; +import javax.xml.soap.SOAPMessage; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +/** + * Simple client that calls the GetFlights operation using SAAJ. + * + * @author Arjen Poutsma + */ +public class GetFlights { + + public static final String NAMESPACE_URI = "http://www.springframework.org/spring-ws/samples/airline/schemas/messages"; + + public static final String PREFIX = "airline"; + + private SOAPConnectionFactory connectionFactory; + + private MessageFactory messageFactory; + + private URL url; + + private TransformerFactory transfomerFactory; + + public GetFlights(String url) throws SOAPException, MalformedURLException { + connectionFactory = SOAPConnectionFactory.newInstance(); + messageFactory = MessageFactory.newInstance(); + transfomerFactory = TransformerFactory.newInstance(); + this.url = new URL(url); + } + + private SOAPMessage createGetFlightsRequest() throws SOAPException { + SOAPMessage message = messageFactory.createMessage(); + SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); + Name getFlightsRequestName = envelope.createName("GetFlightsRequest", PREFIX, NAMESPACE_URI); + SOAPBodyElement getFlightsRequestElement = message.getSOAPBody().addBodyElement(getFlightsRequestName); + Name fromName = envelope.createName("from", PREFIX, NAMESPACE_URI); + SOAPElement fromElement = getFlightsRequestElement.addChildElement(fromName); + fromElement.setValue("AMS"); + Name toName = envelope.createName("to", PREFIX, NAMESPACE_URI); + SOAPElement toElement = getFlightsRequestElement.addChildElement(toName); + toElement.setValue("VCE"); + Name departureDateName = envelope.createName("departureDate", PREFIX, NAMESPACE_URI); + SOAPElement departureDateElement = getFlightsRequestElement.addChildElement(departureDateName); + departureDateElement.setValue("2006-01-31"); + return message; + } + + public void getFlights() throws SOAPException, IOException, TransformerException { + SOAPMessage request = createGetFlightsRequest(); + SOAPConnection connection = connectionFactory.createConnection(); + SOAPMessage response = connection.call(request, url); + if (!response.getSOAPBody().hasFault()) { + writeGetFlightsResponse(response); + } + else { + SOAPFault fault = response.getSOAPBody().getFault(); + System.err.println("Received SOAP Fault"); + System.err.println("SOAP Fault Code: " + fault.getFaultCode()); + System.err.println("SOAP Fault String: " + fault.getFaultString()); + } + } + + private void writeGetFlightsResponse(SOAPMessage message) throws SOAPException, TransformerException { + SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); + Name getFlightsResponseName = envelope.createName("GetFlightsResponse", PREFIX, NAMESPACE_URI); + SOAPBodyElement getFlightsResponseElement = + (SOAPBodyElement) message.getSOAPBody().getChildElements(getFlightsResponseName).next(); + Name flightName = envelope.createName("flight", PREFIX, NAMESPACE_URI); + Iterator iterator = getFlightsResponseElement.getChildElements(flightName); + Transformer transformer = transfomerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + int count = 1; + while (iterator.hasNext()) { + System.out.println("Flight " + count); + System.out.println("--------"); + SOAPElement flightElement = (SOAPElement) iterator.next(); + DOMSource source = new DOMSource(flightElement); + transformer.transform(source, new StreamResult(System.out)); + } + } +} diff --git a/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/GetFrequentFlyerMileage.java b/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/GetFrequentFlyerMileage.java new file mode 100644 index 0000000..8c27d41 --- /dev/null +++ b/airline/client/saaj/src/main/java/org/springframework/ws/samples/airline/client/saaj/GetFrequentFlyerMileage.java @@ -0,0 +1,128 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.saaj; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.xml.soap.MessageFactory; +import javax.xml.soap.Name; +import javax.xml.soap.SOAPBodyElement; +import javax.xml.soap.SOAPConnection; +import javax.xml.soap.SOAPConnectionFactory; +import javax.xml.soap.SOAPEnvelope; +import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPFault; +import javax.xml.soap.SOAPMessage; + +import com.sun.xml.wss.ProcessingContext; +import com.sun.xml.wss.XWSSProcessor; +import com.sun.xml.wss.XWSSProcessorFactory; +import com.sun.xml.wss.XWSSecurityException; +import com.sun.xml.wss.impl.callback.PasswordCallback; +import com.sun.xml.wss.impl.callback.UsernameCallback; + +/** + * Simple client that calls the WS-Security GetFrequentFlyerMileage operation using SAAJ and XWSS. + * + * @author Arjen Poutsma + */ +public class GetFrequentFlyerMileage { + + public static final String NAMESPACE_URI = "http://www.springframework.org/spring-ws/samples/airline/schemas/messages"; + + public static final String PREFIX = "airline"; + + private SOAPConnectionFactory connectionFactory; + + private MessageFactory messageFactory; + + private URL url; + + private XWSSProcessorFactory processorFactory; + + public GetFrequentFlyerMileage(String url) throws SOAPException, MalformedURLException, XWSSecurityException { + connectionFactory = SOAPConnectionFactory.newInstance(); + messageFactory = MessageFactory.newInstance(); + processorFactory = XWSSProcessorFactory.newInstance(); + this.url = new URL(url); + } + + private SOAPMessage createGetMileageRequest() throws SOAPException { + SOAPMessage message = messageFactory.createMessage(); + SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); + Name getFlightsRequestName = envelope.createName("GetFrequentFlyerMileageRequest", GetFrequentFlyerMileage.PREFIX, + GetFrequentFlyerMileage.NAMESPACE_URI); + message.getSOAPBody().addBodyElement(getFlightsRequestName); + return message; + } + + private SOAPMessage secureMessage(SOAPMessage message, final String username, final String password) + throws IOException, XWSSecurityException { + CallbackHandler callbackHandler = new CallbackHandler() { + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof UsernameCallback) { + UsernameCallback callback = (UsernameCallback) callbacks[i]; + callback.setUsername(username); + } + else if (callbacks[i] instanceof PasswordCallback) { + PasswordCallback callback = (PasswordCallback) callbacks[i]; + callback.setPassword(password); + } + else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }; + InputStream policyStream = null; + XWSSProcessor processor = null; + try { + policyStream = getClass().getResourceAsStream("securityPolicy.xml"); + processor = processorFactory.createProcessorForSecurityConfiguration(policyStream, callbackHandler); + } + finally { + if (policyStream != null) { + policyStream.close(); + } + } + ProcessingContext context = processor.createProcessingContext(message); + return processor.secureOutboundMessage(context); + } + + public void getMileage(String username, String password) throws SOAPException, IOException, XWSSecurityException { + SOAPMessage request = createGetMileageRequest(); + request = secureMessage(request, username, password); + SOAPConnection connection = connectionFactory.createConnection(); + SOAPMessage response = connection.call(request, url); + if (!response.getSOAPBody().hasFault()) { + SOAPBodyElement mileage = (SOAPBodyElement) response.getSOAPBody().getChildElements().next(); + System.out.println("'" + username + "' has " + mileage.getValue() + " frequent flyer miles"); + } + else { + SOAPFault fault = response.getSOAPBody().getFault(); + System.err.println("Received SOAP Fault"); + System.err.println("SOAP Fault Code: " + fault.getFaultCode()); + System.err.println("SOAP Fault String: " + fault.getFaultString()); + } + } +} diff --git a/airline/client/saaj/src/main/resources/org/springframework/ws/samples/airline/client/saaj/securityPolicy.xml b/airline/client/saaj/src/main/resources/org/springframework/ws/samples/airline/client/saaj/securityPolicy.xml new file mode 100644 index 0000000..8509073 --- /dev/null +++ b/airline/client/saaj/src/main/resources/org/springframework/ws/samples/airline/client/saaj/securityPolicy.xml @@ -0,0 +1,3 @@ + + + diff --git a/airline/client/spring-ws/build.gradle b/airline/client/spring-ws/build.gradle new file mode 100644 index 0000000..8f86aa7 --- /dev/null +++ b/airline/client/spring-ws/build.gradle @@ -0,0 +1,46 @@ +configurations { + xmlbeans +} + +ext.springWsVersion = '2.1.4.RELEASE' + +task xmlbeans { + ext.schema = "${projectDir}/../../server/src/main/webapp/messages.xsd" + ext.classesDir = "${buildDir}/classes/xmlbeans" + + inputs.files schema + outputs.dir classesDir + + doLast() { + project.ant { + taskdef name: "xmlbeans", + classname: "org.apache.xmlbeans.impl.tool.XMLBean", + classpath: configurations.xmlbeans.asPath + + xmlbeans(classgendir: classesDir, schema: schema, + compiler: "modern", verbose: "false", + classpath: configurations.xmlbeans.asPath) + } + } +} + +dependencies { + compile(files(xmlbeans.classesDir).builtBy(xmlbeans)) + compile("org.apache.xmlbeans:xmlbeans:2.4.0") + compile("org.springframework.ws:spring-ws-core:$springWsVersion") + runtime("org.springframework.ws:spring-ws-security:$springWsVersion") { + exclude group: 'com.sun.xml.wsit', module: 'wsit-rt' + exclude group: 'com.sun.xml.wsit', module: 'xws-security' + } + runtime("com.sun.xml.wss:xws-security:3.0") { + exclude group: 'javax.xml.crypto', module: 'xmldsig' + } + runtime("log4j:log4j:1.2.16") + testCompile("org.springframework.ws:spring-ws-test:$springWsVersion") + xmlbeans("org.apache.xmlbeans:xmlbeans:2.4.0") +} + +task runClient(dependsOn: 'classes', type:JavaExec) { + main = "org.springframework.ws.samples.airline.client.sws.Driver" + classpath = sourceSets.main.runtimeClasspath +} \ No newline at end of file diff --git a/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/Driver.java b/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/Driver.java new file mode 100644 index 0000000..dc2d350 --- /dev/null +++ b/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/Driver.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.sws; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @author Arjen Poutsma + */ +public class Driver { + + public static void main(String[] args) { + ApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "org/springframework/ws/samples/airline/client/sws/applicationContext.xml"); + GetFlights getFlights = applicationContext.getBean("getFlights", GetFlights.class); + getFlights.getFlights(); + + GetFrequentFlyerMileage getFrequentFlyerMileage = applicationContext + .getBean("getFrequentFlyerMileage", GetFrequentFlyerMileage.class); + getFrequentFlyerMileage.getFrequentFlyerMileage(); + + } + +} diff --git a/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/GetFlights.java b/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/GetFlights.java new file mode 100644 index 0000000..a2d3ef5 --- /dev/null +++ b/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/GetFlights.java @@ -0,0 +1,102 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.sws; + +import java.text.SimpleDateFormat; +import java.util.Calendar; + +import org.springframework.springWs.samples.airline.schemas.messages.BookFlightRequestDocument; +import org.springframework.springWs.samples.airline.schemas.messages.BookFlightResponseDocument; +import org.springframework.springWs.samples.airline.schemas.messages.GetFlightsRequestDocument; +import org.springframework.springWs.samples.airline.schemas.messages.GetFlightsResponseDocument; +import org.springframework.springWs.samples.airline.schemas.types.Flight; +import org.springframework.springWs.samples.airline.schemas.types.Name; +import org.springframework.springWs.samples.airline.schemas.types.Ticket; +import org.springframework.ws.WebServiceMessageFactory; +import org.springframework.ws.client.core.support.WebServiceGatewaySupport; + +public class GetFlights extends WebServiceGatewaySupport { + + public GetFlights(WebServiceMessageFactory messageFactory) { + super(messageFactory); + } + + public void getFlights() { + GetFlightsRequestDocument getFlightsRequestDocument = GetFlightsRequestDocument.Factory.newInstance(); + GetFlightsRequestDocument.GetFlightsRequest getFlightsRequest = + getFlightsRequestDocument.addNewGetFlightsRequest(); + getFlightsRequest.setFrom("AMS"); + getFlightsRequest.setTo("VCE"); + Calendar departureDate = Calendar.getInstance(); + departureDate.clear(); + departureDate.set(2006, Calendar.JANUARY, 31); + getFlightsRequest.setDepartureDate(departureDate); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + System.out.println("Requesting flights on " + dateFormat.format(departureDate.getTime())); + GetFlightsResponseDocument getFlightsResponseDocument = + (GetFlightsResponseDocument) getWebServiceTemplate().marshalSendAndReceive(getFlightsRequestDocument); + GetFlightsResponseDocument.GetFlightsResponse response = getFlightsResponseDocument.getGetFlightsResponse(); + System.out.println("Got " + response.sizeOfFlightArray() + " results"); + if (response.sizeOfFlightArray() > 0) { + // Book the first flight using John Doe as a frequent flyer + BookFlightRequestDocument bookFlightRequestDocument = BookFlightRequestDocument.Factory.newInstance(); + BookFlightRequestDocument.BookFlightRequest bookFlightRequest = + bookFlightRequestDocument.addNewBookFlightRequest(); + bookFlightRequest.setFlightNumber(response.getFlightArray(0).getNumber()); + bookFlightRequest.setDepartureTime(response.getFlightArray(0).getDepartureTime()); + BookFlightRequestDocument.BookFlightRequest.Passengers passengers = bookFlightRequest.addNewPassengers(); + passengers.addUsername("john"); + + BookFlightResponseDocument bookFlightResponseDocument = (BookFlightResponseDocument) getWebServiceTemplate() + .marshalSendAndReceive(bookFlightRequestDocument); + Ticket ticket = bookFlightResponseDocument.getBookFlightResponse(); + writeTicket(ticket); + } + } + + private void writeTicket(Ticket ticket) { + System.out.println("Ticket " + ticket.getId()); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + System.out.println("Ticket issue date:\t" + dateFormat.format(ticket.getIssueDate().getTime())); + for (int i = 0; i < ticket.getPassengers().sizeOfPassengerArray(); i++) { + writeName(ticket.getPassengers().getPassengerArray(i)); + + } + writeFlight(ticket.getFlight()); + } + + private void writeName(Name name) { + System.out.println("Passenger Name:"); + System.out.println(name.getFirst() + " " + name.getLast()); + System.out.println("------------"); + } + + private void writeFlight(Flight flight) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + System.out.println(dateFormat.format(flight.getDepartureTime().getTime())); + System.out.println(flight.getNumber() + "\t" + flight.getServiceClass()); + System.out.println("------------"); + System.out.println("Depart:\t" + flight.getFrom().getCode() + "-" + flight.getFrom().getName() + "\t" + + dateFormat.format(flight.getDepartureTime().getTime())); + System.out.println("\t" + flight.getFrom().getCity()); + System.out.println("Arrive:\t" + flight.getTo().getCode() + "-" + flight.getTo().getName() + "\t" + + dateFormat.format(flight.getArrivalTime().getTime())); + System.out.println("\t" + flight.getTo().getCity()); + } + +} diff --git a/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/GetFrequentFlyerMileage.java b/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/GetFrequentFlyerMileage.java new file mode 100644 index 0000000..99021bc --- /dev/null +++ b/airline/client/spring-ws/src/main/java/org/springframework/ws/samples/airline/client/sws/GetFrequentFlyerMileage.java @@ -0,0 +1,40 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.sws; + +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamResult; + +import org.springframework.ws.WebServiceMessageFactory; +import org.springframework.ws.client.core.support.WebServiceGatewaySupport; +import org.springframework.xml.transform.StringSource; + +public class GetFrequentFlyerMileage extends WebServiceGatewaySupport { + + public GetFrequentFlyerMileage(WebServiceMessageFactory messageFactory) { + super(messageFactory); + } + + public void getFrequentFlyerMileage() { + Source source = new StringSource( + ""); + + getWebServiceTemplate().sendSourceAndReceiveToResult(source, new StreamResult(System.out)); + + } + +} diff --git a/airline/client/spring-ws/src/main/resources/log4j.properties b/airline/client/spring-ws/src/main/resources/log4j.properties new file mode 100644 index 0000000..0f73a75 --- /dev/null +++ b/airline/client/spring-ws/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=WARN, stdout +log4j.logger.org.springframework.ws=DEBUG +log4j.logger.org.springframework.xml=DEBUG + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n \ No newline at end of file diff --git a/airline/client/spring-ws/src/main/resources/org/springframework/ws/samples/airline/client/sws/applicationContext.xml b/airline/client/spring-ws/src/main/resources/org/springframework/ws/samples/airline/client/sws/applicationContext.xml new file mode 100644 index 0000000..19c2ed6 --- /dev/null +++ b/airline/client/spring-ws/src/main/resources/org/springframework/ws/samples/airline/client/sws/applicationContext.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/airline/client/spring-ws/src/main/resources/org/springframework/ws/samples/airline/client/sws/securityPolicy.xml b/airline/client/spring-ws/src/main/resources/org/springframework/ws/samples/airline/client/sws/securityPolicy.xml new file mode 100644 index 0000000..8509073 --- /dev/null +++ b/airline/client/spring-ws/src/main/resources/org/springframework/ws/samples/airline/client/sws/securityPolicy.xml @@ -0,0 +1,3 @@ + + + diff --git a/airline/client/spring-ws/src/test/java/org/springframework/ws/samples/airline/client/sws/GetFlightsTest.java b/airline/client/spring-ws/src/test/java/org/springframework/ws/samples/airline/client/sws/GetFlightsTest.java new file mode 100644 index 0000000..39eff19 --- /dev/null +++ b/airline/client/spring-ws/src/test/java/org/springframework/ws/samples/airline/client/sws/GetFlightsTest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.client.sws; + +import java.util.Collections; +import java.util.Map; +import javax.xml.transform.Source; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.xml.transform.StringSource; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.ws.test.client.MockWebServiceServer; +import static org.springframework.ws.test.client.RequestMatchers.*; +import static org.springframework.ws.test.client.ResponseCreators.*; + + +/** + * This test illustrates the use of the client-side testing API, introduced in Spring-WS 2.0. + * + * @author Arjen Poutsma + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("applicationContext.xml") +public class GetFlightsTest { + + @Autowired + GetFlights getFlights; + + private MockWebServiceServer mockServer; + + private static final String MESSAGES_NS = + "http://www.springframework.org/spring-ws/samples/airline/schemas/messages"; + + private static final String TYPES_NS = "http://www.springframework.org/spring-ws/samples/airline/schemas/types"; + + @Before + public void setUpMocks() throws Exception { + mockServer = MockWebServiceServer.createServer(getFlights); + } + + @Test + public void getFlights() throws Exception { + Source getFlightsRequest = new StringSource( + "" + "AMS" + "VCE" + + "2006-01-31" + ""); + + String flightInfo = + "KL1653" + "2006-01-31T10:05:00.000+01:00" + + "AMSSchiphol AirportAmsterdam" + + "2006-01-31T12:25:00.000+01:00" + + "VCEMarco Polo AirportVenice" + + "economy"; + + Source getFlightsResponse = new StringSource( + "" + "" + + flightInfo + "" + ""); + + mockServer.expect(payload(getFlightsRequest)).andRespond(withPayload(getFlightsResponse)); + + Source bookFlightResponse = new StringSource( + "" + "4" + + "2010-07-28" + + "JohnDoe" + + "" + flightInfo + "" + ""); + + Map namespaces = Collections.singletonMap("m", MESSAGES_NS); + + mockServer.expect(xpath("/m:BookFlightRequest/m:flightNumber", namespaces).exists()) + .andExpect(xpath("/m:BookFlightRequest/m:departureTime", namespaces).exists()) + .andExpect(xpath("/m:BookFlightRequest/m:passengers", namespaces).exists()) + .andRespond(withPayload(bookFlightResponse)); + + getFlights.getFlights(); + + mockServer.verify(); + + } + + +} diff --git a/airline/server/build.gradle b/airline/server/build.gradle new file mode 100644 index 0000000..7e6e0e0 --- /dev/null +++ b/airline/server/build.gradle @@ -0,0 +1,147 @@ +configurations { + jaxb + openjpa +} + +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:0.9.9' + } +} + +ext.springVersion = '3.2.4.RELEASE' +ext.springWsVersion = '2.1.4.RELEASE' +ext.springSecVersion = '3.1.0.RELEASE' +ext.tomcatVersion = '7.0.42' + +apply plugin: 'war' +apply plugin: 'tomcat' + + +task genJaxb { + ext.sourcesDir = "${buildDir}/generated-sources/jaxb" + ext.classesDir = "${buildDir}/classes/jaxb" + ext.schema = "${projectDir}/src/main/webapp/messages.xsd" + + inputs.files schema + outputs.dir classesDir + + doLast() { + project.ant { + taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask", + classpath: configurations.jaxb.asPath + mkdir(dir: sourcesDir) + mkdir(dir: classesDir) + + xjc(destdir: sourcesDir, schema: schema, + package: "org.springframework.ws.samples.airline.schema") { + produces(dir: sourcesDir, includes: "**/*.java") + } + + javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true, + debugLevel: "lines,vars,source", + classpath: configurations.jaxb.asPath) { + src(path: sourcesDir) + include(name: "**/*.java") + include(name: "*.java") + } + + copy(todir: classesDir) { + fileset(dir: sourcesDir, erroronmissingdir: false) { + exclude(name: "**/*.java") + } + } + } + } +} + +task enhance { + doLast { + ant.taskdef( name : 'openjpaenhancer', + classpath : project.runtimeClasspath.asPath, + classname : 'org.apache.openjpa.ant.PCEnhancerTask' + ) + + ant.openjpaenhancer( + classpath : project.runtimeClasspath.asPath) + ant.fileset(dir: "${projectDir}/src/main/java/") { + include(name: '**/domain/*.java') + exclude(name: 'ServiceClass.java') + } + } +} + +war { + it.dependsOn enhance +} + +test { + it.dependsOn enhance +} + +tomcatRun { + it.dependsOn enhance + contextPath = 'airline-server' +} + + + + +dependencies { + compile(files(genJaxb.classesDir).builtBy(genJaxb)) + + // Spring-WS + compile("org.springframework.ws:spring-ws-core:$springWsVersion") + compile("org.springframework.ws:spring-ws-security:$springWsVersion") { + exclude group: 'com.sun.xml.wsit', module: 'wsit-rt' + exclude group: 'com.sun.xml.wsit', module: 'xws-security' + } + + runtime("org.springframework.ws:spring-ws-support:$springWsVersion") + + // Spring + runtime("org.springframework:spring-orm:$springVersion") + testCompile("org.springframework:spring-jdbc:$springVersion") + testCompile("org.springframework:spring-test:$springVersion") + + // Spring Security + runtime("org.springframework.security:spring-security-core:$springSecVersion") + runtime("org.springframework.security:spring-security-config:$springSecVersion") + runtime("org.springframework.security:spring-security-web:$springSecVersion") + + // JEE + runtime("javax.servlet:jstl:1.2") + + // ORM + compile("org.apache.openjpa:openjpa:1.2.3") + runtime("commons-dbcp:commons-dbcp:1.2.1") { + exclude group: 'xml-apis', module: 'xml-apis' + exclude group: 'xerces', module: 'xerces' + } + runtime("hsqldb:hsqldb:1.8.0.7") + + // Various + runtime("org.apache.activemq:activemq-core:4.1.2") + runtime("org.apache.derby:derby:10.3.1.4") + runtime("org.apache.ws.xmlschema:xmlschema-core:2.0.2") + runtime("com.sun.xml.wss:xws-security:3.0") { + exclude group: 'javax.xml.crypto', module: 'xmldsig' + } + runtime("joda-time:joda-time-jsptags:1.1.1") + + runtime("log4j:log4j:1.2.16") + + + jaxb "com.sun.xml.bind:jaxb-xjc:2.1.7" + openjpa "org.apache.openjpa:openjpa:1.2.3" + + tomcat("org.apache.tomcat.embed:tomcat-embed-core:$tomcatVersion", + "org.apache.tomcat.embed:tomcat-embed-logging-juli:$tomcatVersion") + tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:$tomcatVersion") { + exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj' + } +} \ No newline at end of file diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/FlightDao.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/FlightDao.java new file mode 100644 index 0000000..dc8c18c --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/FlightDao.java @@ -0,0 +1,36 @@ +/* + * Copyright 2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ws.samples.airline.dao; + +import java.util.List; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.springframework.dao.DataAccessException; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.ServiceClass; + +public interface FlightDao { + + List findFlights(String fromAirportCode, String toAirportCode, Interval interval, ServiceClass serviceClass) + throws DataAccessException; + + Flight getFlight(Long id); + + Flight getFlight(String flightNumber, DateTime departureTime); + + Flight update(Flight flight); +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/FrequentFlyerDao.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/FrequentFlyerDao.java new file mode 100644 index 0000000..e889cc9 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/FrequentFlyerDao.java @@ -0,0 +1,26 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao; + +import org.springframework.dao.DataAccessException; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; + +public interface FrequentFlyerDao { + + FrequentFlyer get(String username) throws DataAccessException; + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/TicketDao.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/TicketDao.java new file mode 100644 index 0000000..e3be0ac --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/TicketDao.java @@ -0,0 +1,26 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao; + +import org.springframework.dao.DataAccessException; +import org.springframework.ws.samples.airline.domain.Ticket; + +public interface TicketDao { + + void save(Ticket ticket) throws DataAccessException; + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/DatabaseInitializer.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/DatabaseInitializer.java new file mode 100644 index 0000000..a4a506a --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/DatabaseInitializer.java @@ -0,0 +1,108 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.ws.samples.airline.domain.Airport; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.domain.ServiceClass; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; + +/** + * A simple class that uses JPA to initialize the database. + * + * @author Arjen Poutsma + * @since 1.5.0 + */ +@Component +public class DatabaseInitializer { + + private static final Log logger = LogFactory.getLog(DatabaseInitializer.class); + + private final TransactionTemplate transactionTemplate; + + @PersistenceContext + private EntityManager entityManager; + + private Airport amsterdam; + + private Airport venice; + + @Autowired + public DatabaseInitializer(PlatformTransactionManager transactionManager) { + this.transactionTemplate = new TransactionTemplate(transactionManager); + } + + @PostConstruct + public void initDatabase() { + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { + logger.info("Initializing Database"); + createAirports(); + createFlights(); + createFrequentFlyers(); + } + }); + } + + private void createAirports() { + amsterdam = new Airport("AMS", "Schiphol Airport", "Amsterdam"); + entityManager.persist(amsterdam); + venice = new Airport("VCE", "Marco Polo Airport", "Venice"); + entityManager.persist(venice); + } + + private void createFlights() { + Flight flight = new Flight(); + flight.setNumber("KL1653"); + flight.setDepartureTime(new DateTime(2006, 1, 31, 10, 5, 0, 0)); + flight.setFrom(amsterdam); + flight.setArrivalTime(new DateTime(2006, 1, 31, 12, 25, 0, 0)); + flight.setTo(venice); + flight.setServiceClass(ServiceClass.ECONOMY); + flight.setSeatsAvailable(5); + flight.setMiles(200); + entityManager.persist(flight); + flight = new Flight(); + flight.setNumber("KL1654"); + flight.setDepartureTime(new DateTime(2006, 2, 5, 12, 40, 0, 0)); + flight.setFrom(venice); + flight.setArrivalTime(new DateTime(2006, 2, 5, 14, 15, 0, 0)); + flight.setTo(amsterdam); + flight.setServiceClass(ServiceClass.ECONOMY); + flight.setSeatsAvailable(5); + flight.setMiles(200); + entityManager.persist(flight); + } + + private void createFrequentFlyers() { + entityManager.persist(new FrequentFlyer("John", "Doe", "john", "changeme")); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaFlightDao.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaFlightDao.java new file mode 100644 index 0000000..72ca105 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaFlightDao.java @@ -0,0 +1,74 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import java.util.List; +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Repository; +import org.springframework.ws.samples.airline.dao.FlightDao; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.ServiceClass; + +@Repository +public class JpaFlightDao implements FlightDao { + + @PersistenceContext + private EntityManager entityManager; + + public List findFlights(String fromAirportCode, + String toAirportCode, + Interval interval, + ServiceClass serviceClass) throws DataAccessException { + Query query = entityManager.createQuery("SELECT f FROM Flight f WHERE f.from.code = :fromParam " + + "AND f.to.code = :toParam AND f.departureTime >= :start AND f.departureTime <= :end AND " + + "f.serviceClass = :class"); + query.setParameter("fromParam", fromAirportCode); + query.setParameter("toParam", toAirportCode); + query.setParameter("start", interval.getStart()); + query.setParameter("end", interval.getEnd()); + query.setParameter("class", serviceClass); + return query.getResultList(); + } + + public Flight getFlight(Long id) { + return entityManager.find(Flight.class, id); + } + + public Flight getFlight(String flightNumber, DateTime departureTime) { + Query query = entityManager + .createQuery("SELECT f FROM Flight f WHERE f.number = :number AND f.departureTime = :departureTime"); + query.setParameter("number", flightNumber); + query.setParameter("departureTime", departureTime); + try { + return (Flight) query.getSingleResult(); + } + catch (NoResultException e) { + return null; + } + } + + public Flight update(Flight flight) { + return entityManager.merge(flight); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaFrequentFlyerDao.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaFrequentFlyerDao.java new file mode 100644 index 0000000..b7cb58d --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaFrequentFlyerDao.java @@ -0,0 +1,46 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; + +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Repository; +import org.springframework.ws.samples.airline.dao.FrequentFlyerDao; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; + +@Repository +public class JpaFrequentFlyerDao implements FrequentFlyerDao { + + @PersistenceContext + private EntityManager entityManager; + + public FrequentFlyer get(String username) throws DataAccessException { + Query query = entityManager.createQuery("SELECT f FROM FrequentFlyer f WHERE f.username = :username"); + query.setParameter("username", username); + try { + return (FrequentFlyer) query.getSingleResult(); + } + catch (NoResultException e) { + return null; + } + } + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaTicketDao.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaTicketDao.java new file mode 100644 index 0000000..ba11a44 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/dao/jpa/JpaTicketDao.java @@ -0,0 +1,36 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Repository; +import org.springframework.ws.samples.airline.dao.TicketDao; +import org.springframework.ws.samples.airline.domain.Ticket; + +@Repository +public class JpaTicketDao implements TicketDao { + + @PersistenceContext + private EntityManager entityManager; + + public void save(Ticket ticket) throws DataAccessException { + entityManager.persist(ticket); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Airport.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Airport.java new file mode 100644 index 0000000..d9951a0 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Airport.java @@ -0,0 +1,74 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.domain; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "AIRPORT") +public class Airport implements Serializable { + + @Id + @Column(name = "CODE") + private String code; + + @Column(name = "NAME") + private String name; + + @Column(name = "CITY") + private String city; + + public Airport() { + } + + public Airport(String code, String name, String city) { + this.code = code; + this.name = name; + this.city = city; + } + + public String getCity() { + return city; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Airport)) { + return false; + } + final Airport that = (Airport) other; + return code.equals(that.code); + } + + public int hashCode() { + return code.hashCode(); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Flight.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Flight.java new file mode 100644 index 0000000..102859f --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Flight.java @@ -0,0 +1,179 @@ +/* + * Copyright 2005, 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ws.samples.airline.domain; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.apache.openjpa.persistence.Persistent; +import org.joda.time.DateTime; + +@Entity +@Table(name = "FLIGHT") +public class Flight implements Serializable { + + @Id + @Column(name = "ID") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "NUMBER") + private String number; + + @Column(name = "DEPARTURE_TIME") + @Persistent + private DateTime departureTime; + + @ManyToOne + @JoinColumn(name = "FROM_AIRPORT_CODE", nullable = false) + private Airport from; + + @Column(name = "ARRIVAL_TIME") + @Persistent + private DateTime arrivalTime; + + @ManyToOne + @JoinColumn(name = "TO_AIRPORT_CODE", nullable = false) + private Airport to; + + @Column(name = "SERVICE_CLASS") + @Enumerated(EnumType.STRING) + private ServiceClass serviceClass; + + @Column(name = "SEATS_AVAILABLE") + private int seatsAvailable; + + @Column(name = "MILES") + private int miles; + + public Flight() { + } + + public Flight(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public DateTime getArrivalTime() { + return arrivalTime; + } + + public void setArrivalTime(DateTime arrivalTime) { + this.arrivalTime = arrivalTime; + } + + public DateTime getDepartureTime() { + return departureTime; + } + + public void setDepartureTime(DateTime departureTime) { + this.departureTime = departureTime; + } + + public Airport getFrom() { + return from; + } + + public void setFrom(Airport from) { + this.from = from; + } + + public int getMiles() { + return miles; + } + + public void setMiles(int miles) { + this.miles = miles; + } + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public int getSeatsAvailable() { + return seatsAvailable; + } + + public void setSeatsAvailable(int seatsAvailable) { + this.seatsAvailable = seatsAvailable; + } + + public ServiceClass getServiceClass() { + return serviceClass; + } + + public void setServiceClass(ServiceClass serviceClass) { + this.serviceClass = serviceClass; + } + + public Airport getTo() { + return to; + } + + public void setTo(Airport to) { + this.to = to; + } + + public void substractSeats(int count) { + seatsAvailable -= count; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final Flight flight = (Flight) o; + + if (!departureTime.equals(flight.departureTime)) { + return false; + } + return number.equals(flight.number); + } + + public int hashCode() { + int result = number.hashCode(); + result = 29 * result + departureTime.hashCode(); + return result; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(getNumber()); + buffer.append(' '); + buffer.append(getDepartureTime().toString()); + return buffer.toString(); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/FrequentFlyer.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/FrequentFlyer.java new file mode 100644 index 0000000..7e428ea --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/FrequentFlyer.java @@ -0,0 +1,97 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.Table; + +@Entity +@Table(name = "FREQUENT_FLYER") +@PrimaryKeyJoinColumn(name = "PASSENGER_ID") +public class FrequentFlyer extends Passenger { + + @Column(name = "USERNAME") + private String username; + + @Column(name = "PASSWORD") + private String password; + + @Column(name = "MILES") + private int miles; + + public FrequentFlyer() { + } + + public FrequentFlyer(String username) { + this.username = username; + } + + public FrequentFlyer(String firstName, String lastName, String username, String password) { + super(firstName, lastName); + this.username = username; + this.password = password; + } + + public int getMiles() { + return miles; + } + + public void setMiles(int miles) { + this.miles = miles; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof FrequentFlyer)) { + return false; + } + final FrequentFlyer that = (FrequentFlyer) other; + return username.equals(that.username); + } + + public int hashCode() { + return username.hashCode(); + } + + public String toString() { + return username; + } + + public void addMiles(int miles) { + this.miles += miles; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Passenger.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Passenger.java new file mode 100644 index 0000000..3aeb628 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Passenger.java @@ -0,0 +1,99 @@ +/* + * Copyright 2005, 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ws.samples.airline.domain; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.Table; + +@Entity +@Table(name = "PASSENGER") +@Inheritance(strategy = InheritanceType.JOINED) +public class Passenger implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ID") + private Long id; + + @Column(name = "FIRST_NAME") + private String firstName; + + @Column(name = "LAST_NAME") + private String lastName; + + public Passenger() { + } + + public Passenger(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public Passenger(Long id, String firstName, String lastName) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() { + return firstName; + } + + public Long getId() { + return id; + } + + public String getLastName() { + return lastName; + } + + public String toString() { + return firstName + " " + lastName; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final Passenger passenger = (Passenger) o; + + if (!getFirstName().equals(passenger.getFirstName())) { + return false; + } + if (!getLastName().equals(passenger.getLastName())) { + return false; + } + + return true; + } + + public int hashCode() { + int result = getFirstName().hashCode(); + result = 29 * result + getLastName().hashCode(); + return result; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/ServiceClass.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/ServiceClass.java new file mode 100644 index 0000000..2c6dc5d --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/ServiceClass.java @@ -0,0 +1,26 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.domain; + +public enum ServiceClass { + + ECONOMY, + + BUSINESS, + + FIRST +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Ticket.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Ticket.java new file mode 100644 index 0000000..da3593f --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/Ticket.java @@ -0,0 +1,97 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.domain; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.apache.openjpa.persistence.Persistent; +import org.joda.time.LocalDate; + +@Entity +@Table(name = "TICKET") +public class Ticket implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "ISSUE_DATE") + @Persistent + private LocalDate issueDate; + + @ManyToOne + @JoinColumn(name = "FLIGHT_ID", nullable = false) + private Flight flight; + + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinTable(name = "PASSENGER_TICKET", + joinColumns = @JoinColumn(name = "TICKET_ID"), + inverseJoinColumns = @JoinColumn(name = "PASSENGER_ID")) + private Set passengers = new HashSet(); + + public Ticket() { + } + + public Ticket(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public Flight getFlight() { + return flight; + } + + public void setFlight(Flight flight) { + this.flight = flight; + } + + public LocalDate getIssueDate() { + return issueDate; + } + + public void setIssueDate(LocalDate issueDate) { + this.issueDate = issueDate; + } + + public Set getPassengers() { + return passengers; + } + + public void setPassengers(Set passengers) { + this.passengers = passengers; + } + + public void addPassenger(Passenger passenger) { + passengers.add(passenger); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/openjpa/DateTimeValueHandler.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/openjpa/DateTimeValueHandler.java new file mode 100644 index 0000000..e3d4842 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/openjpa/DateTimeValueHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.domain.openjpa; + +import java.sql.Timestamp; + +import org.apache.openjpa.jdbc.kernel.JDBCStore; +import org.apache.openjpa.jdbc.meta.JavaSQLTypes; +import org.apache.openjpa.jdbc.meta.ValueMapping; +import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.schema.ColumnIO; +import org.joda.time.DateTime; + +/** + * @author Arjen Poutsma + * @since 1.1.0 + */ +public class DateTimeValueHandler extends AbstractValueHandler { + + private static final DateTimeValueHandler instance = new DateTimeValueHandler(); + + public static DateTimeValueHandler getInstance() { + return instance; + } + + public Column[] map(ValueMapping vm, String name, ColumnIO io, boolean adapt) { + Column col = new Column(); + col.setName(name); + col.setJavaType(JavaSQLTypes.TIMESTAMP); + return new Column[]{col}; + } + + @Override + public Object toDataStoreValue(ValueMapping valueMapping, Object val, JDBCStore jdbcStore) { + return val == null ? null : new Timestamp(((DateTime) val).getMillis()); + } + + @Override + public Object toObjectValue(ValueMapping valueMapping, Object val) { + return val == null ? null : new DateTime(val); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/openjpa/LocalDateValueHandler.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/openjpa/LocalDateValueHandler.java new file mode 100644 index 0000000..b05a46b --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/domain/openjpa/LocalDateValueHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.domain.openjpa; + +import java.sql.Date; + +import org.apache.openjpa.jdbc.kernel.JDBCStore; +import org.apache.openjpa.jdbc.meta.JavaSQLTypes; +import org.apache.openjpa.jdbc.meta.ValueMapping; +import org.apache.openjpa.jdbc.meta.strats.AbstractValueHandler; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.jdbc.schema.ColumnIO; +import org.joda.time.LocalDate; + +/** + * @author Arjen Poutsma + * @since 1.1.0 + */ +public class LocalDateValueHandler extends AbstractValueHandler { + + public Column[] map(ValueMapping vm, String name, ColumnIO io, boolean adapt) { + Column col = new Column(); + col.setName(name); + col.setJavaType(JavaSQLTypes.DATE); + return new Column[]{col}; + } + + @Override + public Object toDataStoreValue(ValueMapping valueMapping, Object val, JDBCStore jdbcStore) { + return val == null ? null : new Date(((LocalDate) val).toDateMidnight().getMillis()); + } + + @Override + public Object toObjectValue(ValueMapping valueMapping, Object val) { + return val == null ? null : new LocalDate(val); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/schema/support/SchemaConversionUtils.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/schema/support/SchemaConversionUtils.java new file mode 100644 index 0000000..4335524 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/schema/support/SchemaConversionUtils.java @@ -0,0 +1,147 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.schema.support; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDate; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.schema.Airport; +import org.springframework.ws.samples.airline.schema.Flight; +import org.springframework.ws.samples.airline.schema.Name; +import org.springframework.ws.samples.airline.schema.ServiceClass; +import org.springframework.ws.samples.airline.schema.Ticket; + +/** @author Arjen Poutsma */ +public abstract class SchemaConversionUtils { + + private SchemaConversionUtils() { + } + + public static Flight toSchemaType(org.springframework.ws.samples.airline.domain.Flight domainFlight) + throws DatatypeConfigurationException { + Flight schemaFlight = new Flight(); + schemaFlight.setNumber(domainFlight.getNumber()); + schemaFlight.setDepartureTime(toXMLGregorianCalendar(domainFlight.getDepartureTime())); + schemaFlight.setFrom(toSchemaType(domainFlight.getFrom())); + schemaFlight.setArrivalTime(toXMLGregorianCalendar(domainFlight.getArrivalTime())); + schemaFlight.setTo(toSchemaType(domainFlight.getTo())); + schemaFlight.setServiceClass(toSchemaType(domainFlight.getServiceClass())); + return schemaFlight; + } + + public static List toSchemaType(List domainFlights) + throws DatatypeConfigurationException { + List schemaFlights = new ArrayList(domainFlights.size()); + for (org.springframework.ws.samples.airline.domain.Flight domainFlight : domainFlights) { + schemaFlights.add(toSchemaType(domainFlight)); + } + return schemaFlights; + } + + public static XMLGregorianCalendar toXMLGregorianCalendar(DateTime dateTime) throws DatatypeConfigurationException { + DatatypeFactory factory = DatatypeFactory.newInstance(); + return factory.newXMLGregorianCalendar(dateTime.toGregorianCalendar()); + } + + public static DateTime toDateTime(XMLGregorianCalendar calendar) { + int timeZoneMinutes = calendar.getTimezone(); + DateTimeZone timeZone = DateTimeZone.forOffsetHoursMinutes(timeZoneMinutes / 60, timeZoneMinutes % 60); + return new DateTime(calendar.getYear(), calendar.getMonth(), calendar.getDay(), calendar.getHour(), + calendar.getMinute(), calendar.getSecond(), calendar.getMillisecond(), timeZone); + } + + public static XMLGregorianCalendar toXMLGregorianCalendar(LocalDate localDate) + throws DatatypeConfigurationException { + DatatypeFactory factory = DatatypeFactory.newInstance(); + return factory.newXMLGregorianCalendarDate(localDate.getYear(), localDate.getMonthOfYear(), + localDate.getDayOfMonth(), DatatypeConstants.FIELD_UNDEFINED); + } + + public static LocalDate toLocalDate(XMLGregorianCalendar calendar) { + return new LocalDate(calendar.getYear(), calendar.getMonth(), calendar.getDay()); + } + + public static Airport toSchemaType(org.springframework.ws.samples.airline.domain.Airport domainAirport) { + if (domainAirport == null) { + return null; + } + Airport schemaAirport = new Airport(); + schemaAirport.setCode(domainAirport.getCode()); + schemaAirport.setName(domainAirport.getName()); + schemaAirport.setCity(domainAirport.getCity()); + return schemaAirport; + } + + public static ServiceClass toSchemaType(org.springframework.ws.samples.airline.domain.ServiceClass domainServiceClass) { + switch (domainServiceClass) { + case BUSINESS: + return ServiceClass.BUSINESS; + case ECONOMY: + return ServiceClass.ECONOMY; + case FIRST: + return ServiceClass.FIRST; + default: + throw new IllegalArgumentException("Invalid domain service class: [" + domainServiceClass + "]"); + } + } + + public static org.springframework.ws.samples.airline.domain.ServiceClass toDomainType(ServiceClass schemaServiceClass) { + if (schemaServiceClass == null) { + return null; + } + switch (schemaServiceClass) { + case BUSINESS: + return org.springframework.ws.samples.airline.domain.ServiceClass.BUSINESS; + case ECONOMY: + return org.springframework.ws.samples.airline.domain.ServiceClass.ECONOMY; + case FIRST: + return org.springframework.ws.samples.airline.domain.ServiceClass.FIRST; + default: + throw new IllegalArgumentException("Invalid schema service class: [" + schemaServiceClass + "]"); + } + } + + public static Name toSchemaType(org.springframework.ws.samples.airline.domain.Passenger passenger) { + Name name = new Name(); + name.setFirst(passenger.getFirstName()); + name.setLast(passenger.getLastName()); + return name; + } + + public static Ticket toSchemaType(org.springframework.ws.samples.airline.domain.Ticket domainTicket) + throws DatatypeConfigurationException { + Ticket schemaTicket = new Ticket(); + schemaTicket.setId(domainTicket.getId()); + schemaTicket.setFlight(toSchemaType(domainTicket.getFlight())); + schemaTicket.setIssueDate(toXMLGregorianCalendar(domainTicket.getIssueDate())); + if (!domainTicket.getPassengers().isEmpty()) { + schemaTicket.setPassengers(new Ticket.Passengers()); + } + for (Passenger passenger : domainTicket.getPassengers()) { + schemaTicket.getPassengers().getPassenger().add(toSchemaType(passenger)); + } + return schemaTicket; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/security/FrequentFlyerDetails.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/FrequentFlyerDetails.java new file mode 100644 index 0000000..6bda1d7 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/FrequentFlyerDetails.java @@ -0,0 +1,82 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.security; + +import java.util.ArrayList; +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.GrantedAuthorityImpl; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; + +/** + * A wrapper around an FrequentFlyer which provides extra functionality needed to implement the + * UserDetails interface. + * + * @author Arjen Poutsma + */ +public class FrequentFlyerDetails implements UserDetails { + + private FrequentFlyer frequentFlyer; + + public static final Collection GRANTED_AUTHORITIES = + new ArrayList(); + { + GRANTED_AUTHORITIES.add(new GrantedAuthorityImpl("ROLE_FREQUENT_FLYER")); + }; + + public FrequentFlyerDetails(FrequentFlyer frequentFlyer) { + this.frequentFlyer = frequentFlyer; + } + + public Collection getAuthorities() { + return GRANTED_AUTHORITIES; + } + + public String getPassword() { + return frequentFlyer.getPassword(); + } + + public String getUsername() { + return frequentFlyer.getUsername(); + } + + public boolean isAccountNonExpired() { + return true; + } + + public boolean isAccountNonLocked() { + return true; + } + + public boolean isCredentialsNonExpired() { + return true; + } + + public boolean isEnabled() { + return true; + } + + public FrequentFlyer getFrequentFlyer() { + return frequentFlyer; + } + + public String toString() { + return frequentFlyer.toString(); + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/security/FrequentFlyerSecurityService.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/FrequentFlyerSecurityService.java new file mode 100644 index 0000000..5f442ea --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/FrequentFlyerSecurityService.java @@ -0,0 +1,45 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.security; + +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.service.NoSuchFrequentFlyerException; + +/** + * Defines the business logic for handling frequent flyers. + * + * @author Arjen Poutsma + */ +public interface FrequentFlyerSecurityService { + + /** + * Returns the FrequentFlyer with the given username. + * + * @param username the username + * @return the frequent flyer with the given username, or null if not found + * @throws NoSuchFrequentFlyerException when the frequent flyer cannot be found + */ + FrequentFlyer getFrequentFlyer(String username) throws NoSuchFrequentFlyerException; + + /** + * Returns the FrequentFlyer that is currently logged in. + * + * @return the frequent flyer that is currently logged in, or null if not found + */ + FrequentFlyer getCurrentlyAuthenticatedFrequentFlyer(); + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/security/SpringFrequentFlyerSecurityService.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/SpringFrequentFlyerSecurityService.java new file mode 100644 index 0000000..355d2a7 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/SpringFrequentFlyerSecurityService.java @@ -0,0 +1,86 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.ws.samples.airline.dao.FrequentFlyerDao; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.service.NoSuchFrequentFlyerException; + +/** + * Implementation of the FrequentFlyerSecurityService that uses Spring Security. + * + * @author Arjen Poutsma + */ +public class SpringFrequentFlyerSecurityService implements FrequentFlyerSecurityService, UserDetailsService { + + private FrequentFlyerDao frequentFlyerDao; + + @Autowired + public SpringFrequentFlyerSecurityService(FrequentFlyerDao frequentFlyerDao) { + this.frequentFlyerDao = frequentFlyerDao; + } + + @Transactional + public FrequentFlyer getCurrentlyAuthenticatedFrequentFlyer() { + SecurityContext context = SecurityContextHolder.getContext(); + Authentication authentication = context.getAuthentication(); + if (authentication != null) { + if (authentication.getPrincipal() instanceof FrequentFlyerDetails) { + FrequentFlyerDetails details = (FrequentFlyerDetails) authentication.getPrincipal(); + return details.getFrequentFlyer(); + } + else { + return (FrequentFlyer) authentication.getPrincipal(); + } + } + else { + return null; + } + } + + @Transactional + public FrequentFlyer getFrequentFlyer(String username) throws NoSuchFrequentFlyerException { + FrequentFlyer frequentFlyer = frequentFlyerDao.get(username); + if (frequentFlyer != null) { + return frequentFlyer; + } + else { + throw new NoSuchFrequentFlyerException(username); + } + } + + @Transactional + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { + FrequentFlyer frequentFlyer = frequentFlyerDao.get(username); + if (frequentFlyer != null) { + return new FrequentFlyerDetails(frequentFlyer); + } + else { + throw new UsernameNotFoundException("Frequent flyer '" + username + "' not found"); + } + } + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/security/StubFrequentFlyerSecurityService.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/StubFrequentFlyerSecurityService.java new file mode 100644 index 0000000..b4c1f56 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/security/StubFrequentFlyerSecurityService.java @@ -0,0 +1,50 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.security; + +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.service.NoSuchFrequentFlyerException; + +/** + * Stub implementation of FrequentFlyerSecurityService. This implementation is used by default by {@link + * org.springframework.ws.samples.airline.service.impl.AirlineServiceImpl}, to allow it to run without depending on + * Spring Security. + * + * @author Arjen Poutsma + */ +public class StubFrequentFlyerSecurityService implements FrequentFlyerSecurityService { + + private FrequentFlyer john; + + public StubFrequentFlyerSecurityService() { + john = new FrequentFlyer("John", "Doe", "john", "changeme"); + john.setMiles(10); + } + + public FrequentFlyer getFrequentFlyer(String username) throws NoSuchFrequentFlyerException { + if (john.getUsername().equals(username)) { + return john; + } + else { + throw new NoSuchFrequentFlyerException(username); + } + } + + public FrequentFlyer getCurrentlyAuthenticatedFrequentFlyer() { + return john; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/service/AirlineService.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/AirlineService.java new file mode 100644 index 0000000..851a5a4 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/AirlineService.java @@ -0,0 +1,84 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ws.samples.airline.service; + +import java.util.List; + +import org.joda.time.DateTime; +import org.joda.time.LocalDate; + +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.domain.ServiceClass; +import org.springframework.ws.samples.airline.domain.Ticket; + +/** + * Defines the business logic of the Airline application. + * + * @author Arjen Poutsma + */ +public interface AirlineService { + + /** + * Returns a single Flight with the given id. + * + * @param id the flight identifier + * @return the flight + * @throws NoSuchFlightException if a flight with the specified flight iddoes not exist + */ + Flight getFlight(Long id) throws NoSuchFlightException; + + /** + * Returns a list of Flight objects that fall within the specified criteria. + * + * @param fromAirportCode the three-letter airport code to get flights from + * @param toAirportCode the three-letter airport code to get flights to + * @param departureDate the date of the flights + * @param serviceClass the desired service class level. May be null + * @return a list of flights + */ + List getFlights(String fromAirportCode, + String toAirportCode, + LocalDate departureDate, + ServiceClass serviceClass); + + /** + * Books a single flight for a number of passengers. Passengers can be either specified by name or by frequent flyer + * username. If a {@link FrequentFlyer} is specified, the first and last name are looked up in the database. + * + * @param flightNumber the number of the flight to book + * @param departureTime the departure time of the flight to book + * @param passengers the list of passengers for the flight to book. Can be either {@link Passenger} objects with + * a first and last name, or {@link FrequentFlyer} objects with a username. + * @return the created ticket + * @throws NoSuchFlightException if a flight with the specified flight number and departure time does not + * exist + * @throws NoSeatAvailableException if not enough seats are available for the flight + * @throws NoSuchFrequentFlyerException if a specified {@link FrequentFlyer} cannot be found + * @see org.springframework.ws.samples.airline.domain.Passenger + * @see org.springframework.ws.samples.airline.domain.FrequentFlyer + */ + Ticket bookFlight(String flightNumber, DateTime departureTime, List passengers) + throws NoSuchFlightException, NoSeatAvailableException, NoSuchFrequentFlyerException; + + /** + * Returns the amount of frequent flyer award miles for the currently logged in frequent flyer. + * + * @return the amount of frequent flyer miles + */ + int getFrequentFlyerMileage(); +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSeatAvailableException.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSeatAvailableException.java new file mode 100644 index 0000000..d04c62d --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSeatAvailableException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.service; + +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.soap.server.endpoint.annotation.FaultCode; +import org.springframework.ws.soap.server.endpoint.annotation.SoapFault; + +/** + * Exception thrown when not enough seats are available for a flight. + * + * @author Arjen Poutsma + */ +@SoapFault(faultCode = FaultCode.SERVER) +public class NoSeatAvailableException extends Exception { + + private Flight flight; + + public NoSeatAvailableException(Flight flight) { + super("Flight [" + flight + "] has not more seats available"); + this.flight = flight; + } + + public Flight getFlight() { + return flight; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSuchFlightException.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSuchFlightException.java new file mode 100644 index 0000000..b79db40 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSuchFlightException.java @@ -0,0 +1,52 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.service; + +import org.joda.time.DateTime; +import org.springframework.ws.soap.server.endpoint.annotation.FaultCode; +import org.springframework.ws.soap.server.endpoint.annotation.SoapFault; + +/** + * Exception thrown when a specified flight cannot be found. + * + * @author Arjen Poutsma + */ +@SoapFault(faultCode = FaultCode.CLIENT) +public class NoSuchFlightException extends Exception { + + private String flightNumber; + + private DateTime departureTime; + + public NoSuchFlightException(String flightNumber, DateTime departureTime) { + super("No flight with number [" + flightNumber + "] and departure time [" + departureTime + "]"); + this.flightNumber = flightNumber; + this.departureTime = departureTime; + } + + public NoSuchFlightException(Long id) { + super("No flight with id [" + id + "]"); + } + + public String getFlightNumber() { + return flightNumber; + } + + public DateTime getDepartureTime() { + return departureTime; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSuchFrequentFlyerException.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSuchFrequentFlyerException.java new file mode 100644 index 0000000..3b8a821 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/NoSuchFrequentFlyerException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.service; + +import org.springframework.ws.soap.server.endpoint.annotation.FaultCode; +import org.springframework.ws.soap.server.endpoint.annotation.SoapFault; + +/** + * Exception thrown when a specified frequent flyer cannot be found. + * + * @author Rossen Stoyanchev + * @author Arjen Poutsma + */ +@SoapFault(faultCode = FaultCode.CLIENT) +public class NoSuchFrequentFlyerException extends Exception { + + private String username; + + public NoSuchFrequentFlyerException(String username) { + super("No frequent flyer with name [" + username + "]"); + this.username = username; + } + + public String getusername() { + return username; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/service/impl/AirlineServiceImpl.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/impl/AirlineServiceImpl.java new file mode 100644 index 0000000..c9cfec1 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/service/impl/AirlineServiceImpl.java @@ -0,0 +1,146 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ws.samples.airline.service.impl; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.annotation.Secured; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.ws.samples.airline.dao.FlightDao; +import org.springframework.ws.samples.airline.dao.TicketDao; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.domain.ServiceClass; +import org.springframework.ws.samples.airline.domain.Ticket; +import org.springframework.ws.samples.airline.security.FrequentFlyerSecurityService; +import org.springframework.ws.samples.airline.security.StubFrequentFlyerSecurityService; +import org.springframework.ws.samples.airline.service.AirlineService; +import org.springframework.ws.samples.airline.service.NoSeatAvailableException; +import org.springframework.ws.samples.airline.service.NoSuchFlightException; +import org.springframework.ws.samples.airline.service.NoSuchFrequentFlyerException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; + +/** + * Default implementation of the AirlineService interface. + * + * @author Arjen Poutsma + */ +@Service +@Transactional(readOnly = true) +public class AirlineServiceImpl implements AirlineService { + + private static final Log logger = LogFactory.getLog(AirlineServiceImpl.class); + + private FlightDao flightDao; + + private TicketDao ticketDao; + + private FrequentFlyerSecurityService frequentFlyerSecurityService = new StubFrequentFlyerSecurityService(); + + @Autowired + public AirlineServiceImpl(FlightDao flightDao, TicketDao ticketDao) { + this.flightDao = flightDao; + this.ticketDao = ticketDao; + } + + @Autowired(required = false) + public void setFrequentFlyerSecurityService(FrequentFlyerSecurityService frequentFlyerSecurityService) { + this.frequentFlyerSecurityService = frequentFlyerSecurityService; + } + + @Transactional(readOnly = false, + rollbackFor = {NoSuchFlightException.class, NoSeatAvailableException.class, NoSuchFrequentFlyerException.class}) + public Ticket bookFlight(String flightNumber, DateTime departureTime, List passengers) + throws NoSuchFlightException, NoSeatAvailableException, NoSuchFrequentFlyerException { + Assert.notEmpty(passengers, "No passengers given"); + if (logger.isDebugEnabled()) { + logger.debug("Booking flight '" + flightNumber + "' on '" + departureTime + "' for " + passengers); + } + Flight flight = flightDao.getFlight(flightNumber, departureTime); + if (flight == null) { + throw new NoSuchFlightException(flightNumber, departureTime); + } + else if (flight.getSeatsAvailable() < passengers.size()) { + throw new NoSeatAvailableException(flight); + } + Ticket ticket = new Ticket(); + ticket.setIssueDate(new LocalDate()); + ticket.setFlight(flight); + for (Passenger passenger : passengers) { + // frequent flyer service is not required + if (passenger instanceof FrequentFlyer && frequentFlyerSecurityService != null) { + String username = ((FrequentFlyer) passenger).getUsername(); + Assert.hasLength(username, "No username specified"); + FrequentFlyer frequentFlyer = frequentFlyerSecurityService.getFrequentFlyer(username); + frequentFlyer.addMiles(flight.getMiles()); + ticket.addPassenger(frequentFlyer); + } + else { + ticket.addPassenger(passenger); + } + } + flight.substractSeats(passengers.size()); + flightDao.update(flight); + ticketDao.save(ticket); + return ticket; + } + + public Flight getFlight(Long id) throws NoSuchFlightException { + Flight flight = flightDao.getFlight(id); + if (flight != null) { + return flight; + } + else { + throw new NoSuchFlightException(id); + } + } + + public List getFlights(String fromAirportCode, + String toAirportCode, + LocalDate departureDate, + ServiceClass serviceClass) { + if (serviceClass == null) { + serviceClass = ServiceClass.ECONOMY; + } + if (logger.isDebugEnabled()) { + logger.debug( + "Getting flights from '" + fromAirportCode + "' to '" + toAirportCode + "' on " + departureDate); + } + List flights = + flightDao.findFlights(fromAirportCode, toAirportCode, departureDate.toInterval(), serviceClass); + if (logger.isDebugEnabled()) { + logger.debug("Returning " + flights.size() + " flights"); + } + return flights; + } + + @Secured({"ROLE_FREQUENT_FLYER"}) + public int getFrequentFlyerMileage() { + if (logger.isDebugEnabled()) { + logger.debug("Using " + frequentFlyerSecurityService + " for security"); + } + FrequentFlyer frequentFlyer = frequentFlyerSecurityService.getCurrentlyAuthenticatedFrequentFlyer(); + return frequentFlyer != null ? frequentFlyer.getMiles() : 0; + } +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/web/FlightsController.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/web/FlightsController.java new file mode 100644 index 0000000..19e8525 --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/web/FlightsController.java @@ -0,0 +1,80 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.web; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.ServiceClass; +import org.springframework.ws.samples.airline.service.AirlineService; +import org.springframework.ws.samples.airline.service.NoSuchFlightException; + +import org.joda.time.LocalDate; + +/** @author Arjen Poutsma */ +@Controller +@RequestMapping("/flights") +public class FlightsController { + + private AirlineService airlineService; + + @Autowired + public FlightsController(AirlineService airlineService) { + Assert.notNull(airlineService, "'airlineService' must not be null"); + this.airlineService = airlineService; + } + + @RequestMapping + public String flightList(@RequestParam(value = "from", required = false)String fromAirportCode, + @RequestParam(value = "to", required = false)String toAirportCode, + @RequestParam(value = "departureDate", required = false)String departureDateString, + @RequestParam(value = "serviceClass", required = false)String serviceClassString, + ModelMap model) { + if (!StringUtils.hasLength(departureDateString)) { + departureDateString = new LocalDate().toString(); + } + if (!StringUtils.hasLength(serviceClassString)) { + serviceClassString = "ECONOMY"; + } + ServiceClass serviceClass = ServiceClass.valueOf(serviceClassString); + LocalDate departureDate = new LocalDate(departureDateString); + + if (StringUtils.hasLength(fromAirportCode) && StringUtils.hasLength(toAirportCode)) { + model.addAttribute("from", fromAirportCode); + model.addAttribute("to", toAirportCode); + model.addAttribute("departureDate", departureDateString); + model.addAttribute("serviceClass", serviceClassString); + model.addAttribute("flights", + airlineService.getFlights(fromAirportCode, toAirportCode, departureDate, serviceClass)); + } + return "flights"; + } + + @RequestMapping(value = "{id}") + public String singleFlight(@PathVariable("id") long id, ModelMap model) throws NoSuchFlightException { + Flight flight = airlineService.getFlight(id); + model.addAttribute(flight); + return "flight"; + } + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/ws/AirlineEndpoint.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/ws/AirlineEndpoint.java new file mode 100644 index 0000000..e0c9f8d --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/ws/AirlineEndpoint.java @@ -0,0 +1,179 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.ws; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.domain.ServiceClass; +import org.springframework.ws.samples.airline.schema.BookFlightRequest; +import org.springframework.ws.samples.airline.schema.GetFlightsResponse; +import org.springframework.ws.samples.airline.schema.Name; +import org.springframework.ws.samples.airline.schema.ObjectFactory; +import org.springframework.ws.samples.airline.schema.Ticket; +import org.springframework.ws.samples.airline.schema.support.SchemaConversionUtils; +import org.springframework.ws.samples.airline.service.AirlineService; +import org.springframework.ws.samples.airline.service.NoSeatAvailableException; +import org.springframework.ws.samples.airline.service.NoSuchFlightException; +import org.springframework.ws.samples.airline.service.NoSuchFrequentFlyerException; +import org.springframework.ws.server.endpoint.annotation.Endpoint; +import org.springframework.ws.server.endpoint.annotation.Namespace; +import org.springframework.ws.server.endpoint.annotation.PayloadRoot; +import org.springframework.ws.server.endpoint.annotation.RequestPayload; +import org.springframework.ws.server.endpoint.annotation.ResponsePayload; +import org.springframework.ws.server.endpoint.annotation.XPathParam; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import static org.springframework.ws.samples.airline.ws.AirlineWebServiceConstants.*; + +/** + * Endpoint that handles the Airline Web Service messages using a combination of JAXB2 marshalling and XPath + * expressions. + * + * @author Arjen Poutsma + */ +@Endpoint +public class AirlineEndpoint { + + private static final Log logger = LogFactory.getLog(AirlineEndpoint.class); + + private final ObjectFactory objectFactory = new ObjectFactory(); + + private final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + + private final AirlineService airlineService; + + @Autowired + public AirlineEndpoint(AirlineService airlineService) { + this.airlineService = airlineService; + } + + /** + * This endpoint method uses a combination of XPath expressions and marshalling to handle message with a + * <GetFlightsRequest> payload. + * + * @param from the from airport + * @param to the to airport + * @param departureDateString the string representation of the departure date + * @param serviceClassString the string representation of the service class + * @return the JAXB2 representation of a <GetFlightsResponse> + */ + @PayloadRoot(localPart = GET_FLIGHTS_REQUEST, namespace = MESSAGES_NAMESPACE) + @Namespace(prefix = "m", uri = MESSAGES_NAMESPACE) + @ResponsePayload + public GetFlightsResponse getFlights(@XPathParam("//m:from") String from, + @XPathParam("//m:to") String to, + @XPathParam("//m:departureDate") String departureDateString, + @XPathParam("//m:serviceClass") String serviceClassString) + throws DatatypeConfigurationException { + if (logger.isDebugEnabled()) { + logger.debug("Received GetFlightsRequest '" + from + "' to '" + to + "' on " + departureDateString); + } + LocalDate departureDate = new LocalDate(departureDateString); + ServiceClass serviceClass = null; + if (StringUtils.hasLength(serviceClassString)) { + serviceClass = ServiceClass.valueOf(serviceClassString.toUpperCase()); + } + List flights = + airlineService.getFlights(from, to, departureDate, serviceClass); + + GetFlightsResponse response = objectFactory.createGetFlightsResponse(); + for (org.springframework.ws.samples.airline.domain.Flight domainFlight : flights) { + response.getFlight().add(SchemaConversionUtils.toSchemaType(domainFlight)); + } + return response; + } + + /** + * This endpoint method uses marshalling to handle message with a <BookFlightRequest> payload. + * + * @param request the JAXB2 representation of a <BookFlightRequest> + * @return the JAXB2 representation of a <BookFlightResponse> + */ + @PayloadRoot(localPart = BOOK_FLIGHT_REQUEST, namespace = MESSAGES_NAMESPACE) + @ResponsePayload + public JAXBElement bookFlight(@RequestPayload BookFlightRequest request) + throws NoSeatAvailableException, DatatypeConfigurationException, NoSuchFlightException, + NoSuchFrequentFlyerException { + if (logger.isDebugEnabled()) { + logger.debug("Received BookingFlightRequest '" + request.getFlightNumber() + "' on '" + + request.getDepartureTime() + "' for " + request.getPassengers().getPassengerOrUsername()); + } + Ticket ticket = bookSchemaFlight(request.getFlightNumber(), request.getDepartureTime(), + request.getPassengers().getPassengerOrUsername()); + return objectFactory.createBookFlightResponse(ticket); + } + + /** + * Converts between the domain and schema types. + */ + private Ticket bookSchemaFlight(String flightNumber, + XMLGregorianCalendar xmlDepartureTime, + List passengerOrUsernameList) + throws NoSeatAvailableException, NoSuchFlightException, NoSuchFrequentFlyerException, + DatatypeConfigurationException { + DateTime departureTime = SchemaConversionUtils.toDateTime(xmlDepartureTime); + List passengers = new ArrayList(passengerOrUsernameList.size()); + for (Iterator iterator = passengerOrUsernameList.iterator(); iterator.hasNext();) { + Object passengerOrUsername = iterator.next(); + if (passengerOrUsername instanceof Name) { + Name passengerName = (Name) passengerOrUsername; + Passenger passenger = new Passenger(passengerName.getFirst(), passengerName.getLast()); + passengers.add(passenger); + } + else if (passengerOrUsername instanceof String) { + String frequentFlyerUsername = (String) passengerOrUsername; + FrequentFlyer frequentFlyer = new FrequentFlyer(frequentFlyerUsername); + passengers.add(frequentFlyer); + } + } + org.springframework.ws.samples.airline.domain.Ticket domainTicket = + airlineService.bookFlight(flightNumber, departureTime, passengers); + return SchemaConversionUtils.toSchemaType(domainTicket); + } + + @PayloadRoot(localPart = GET_FREQUENT_FLYER_MILEAGE_REQUEST, namespace = MESSAGES_NAMESPACE) + @ResponsePayload + public Element getFrequentFlyerMileage() throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("Received GetFrequentFlyerMileageRequest"); + } + int mileage = airlineService.getFrequentFlyerMileage(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.newDocument(); + Element response = document.createElementNS(MESSAGES_NAMESPACE, GET_FREQUENT_FLYER_MILEAGE_RESPONSE); + response.setTextContent(Integer.toString(mileage)); + return response; + } + +} diff --git a/airline/server/src/main/java/org/springframework/ws/samples/airline/ws/AirlineWebServiceConstants.java b/airline/server/src/main/java/org/springframework/ws/samples/airline/ws/AirlineWebServiceConstants.java new file mode 100644 index 0000000..b951c9b --- /dev/null +++ b/airline/server/src/main/java/org/springframework/ws/samples/airline/ws/AirlineWebServiceConstants.java @@ -0,0 +1,31 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.ws; + +/** @author Arjen Poutsma */ +public interface AirlineWebServiceConstants { + + String BOOK_FLIGHT_REQUEST = "BookFlightRequest"; + + String GET_FLIGHTS_REQUEST = "GetFlightsRequest"; + + String GET_FREQUENT_FLYER_MILEAGE_RESPONSE = "GetFrequentFlyerMileageResponse"; + + String MESSAGES_NAMESPACE = "http://www.springframework.org/spring-ws/samples/airline/schemas/messages"; + + String GET_FREQUENT_FLYER_MILEAGE_REQUEST = "GetFrequentFlyerMileageRequest"; +} diff --git a/airline/server/src/main/resources/META-INF/persistence.xml b/airline/server/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..5f67178 --- /dev/null +++ b/airline/server/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,14 @@ + + + + org.apache.openjpa.persistence.PersistenceProviderImpl + org.springframework.ws.samples.airline.domain.Airport + org.springframework.ws.samples.airline.domain.Flight + org.springframework.ws.samples.airline.domain.FrequentFlyer + org.springframework.ws.samples.airline.domain.Passenger + org.springframework.ws.samples.airline.domain.Ticket + + + diff --git a/airline/server/src/main/resources/log4j.properties b/airline/server/src/main/resources/log4j.properties new file mode 100644 index 0000000..c44d7a4 --- /dev/null +++ b/airline/server/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootLogger=WARN, stdout +log4j.logger.org.springframework.ws=DEBUG +log4j.logger.org.springframework.xml=DEBUG +log4j.logger.openjpa=INFO + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n \ No newline at end of file diff --git a/airline/server/src/main/resources/org/springframework/ws/samples/airline/dao/jpa/applicationContext-jpa.xml b/airline/server/src/main/resources/org/springframework/ws/samples/airline/dao/jpa/applicationContext-jpa.xml new file mode 100644 index 0000000..d322dc5 --- /dev/null +++ b/airline/server/src/main/resources/org/springframework/ws/samples/airline/dao/jpa/applicationContext-jpa.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + commons + buildSchema + + FieldStrategies='org.joda.time.DateTime=org.springframework.ws.samples.airline.domain.openjpa.DateTimeValueHandler, + org.joda.time.LocalDate=org.springframework.ws.samples.airline.domain.openjpa.LocalDateValueHandler' + + org.apache.openjpa.jdbc.sql.HSQLDictionary + + + + + + + + + + + + + + + + diff --git a/airline/server/src/main/resources/org/springframework/ws/samples/airline/jms/applicationContext-jms.xml b/airline/server/src/main/resources/org/springframework/ws/samples/airline/jms/applicationContext-jms.xml new file mode 100644 index 0000000..aae02e2 --- /dev/null +++ b/airline/server/src/main/resources/org/springframework/ws/samples/airline/jms/applicationContext-jms.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/airline/server/src/main/resources/org/springframework/ws/samples/airline/security/applicationContext-security.xml b/airline/server/src/main/resources/org/springframework/ws/samples/airline/security/applicationContext-security.xml new file mode 100644 index 0000000..e464450 --- /dev/null +++ b/airline/server/src/main/resources/org/springframework/ws/samples/airline/security/applicationContext-security.xml @@ -0,0 +1,26 @@ + + + + + This application context contains the WS-Security and Spring Security beans. + + + + + + + + + + + A security service used to obtain Frequent Flyer information. + + + + + \ No newline at end of file diff --git a/airline/server/src/main/resources/org/springframework/ws/samples/airline/security/securityPolicy.xml b/airline/server/src/main/resources/org/springframework/ws/samples/airline/security/securityPolicy.xml new file mode 100644 index 0000000..b6d644b --- /dev/null +++ b/airline/server/src/main/resources/org/springframework/ws/samples/airline/security/securityPolicy.xml @@ -0,0 +1,3 @@ + + + diff --git a/airline/server/src/main/resources/org/springframework/ws/samples/airline/service/applicationContext.xml b/airline/server/src/main/resources/org/springframework/ws/samples/airline/service/applicationContext.xml new file mode 100644 index 0000000..a24f49e --- /dev/null +++ b/airline/server/src/main/resources/org/springframework/ws/samples/airline/service/applicationContext.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/airline/server/src/main/resources/org/springframework/ws/samples/airline/ws/applicationContext-ws.xml b/airline/server/src/main/resources/org/springframework/ws/samples/airline/ws/applicationContext-ws.xml new file mode 100644 index 0000000..0fe62f8 --- /dev/null +++ b/airline/server/src/main/resources/org/springframework/ws/samples/airline/ws/applicationContext-ws.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This bean wrap the messages.xsd (which imports types.xsd), and inlines them as a one. + + + + + + diff --git a/airline/server/src/main/webapp/WEB-INF/jsp/error.jsp b/airline/server/src/main/webapp/WEB-INF/jsp/error.jsp new file mode 100644 index 0000000..d7654d9 --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/jsp/error.jsp @@ -0,0 +1,13 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + +<% + Exception ex = (Exception) request.getAttribute("exception"); +%> +<%= ex.getMessage() %> + + +

<%= ex.getMessage() %> +

+Flights + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/WEB-INF/jsp/flight.jsp b/airline/server/src/main/webapp/WEB-INF/jsp/flight.jsp new file mode 100644 index 0000000..7c76fc0 --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/jsp/flight.jsp @@ -0,0 +1,47 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> + +Flights + + + + + + + + + + + + + + + + + + + + + + +
Number: + +
From: + + ( + + ) +
Departure: + +
To: + + ( + + ) +
Departure: + +
+Flights + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/WEB-INF/jsp/flights.jsp b/airline/server/src/main/webapp/WEB-INF/jsp/flights.jsp new file mode 100644 index 0000000..441c92e --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/jsp/flights.jsp @@ -0,0 +1,73 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> +<%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %> + +Flights + +
+ + + + + + + + + + + + + + + + + + + + +
From:
To:"/>
Departure Date:"/>
Service Class: + +
+ +
+ + + + + + + + + + + + + + +
NumberDepartsArrives
+ + + + + + + ( + + ) + + + + ( + + ) + +
+
+
+ + \ No newline at end of file diff --git a/airline/server/src/main/webapp/WEB-INF/jsp/noSuchFlight.jsp b/airline/server/src/main/webapp/WEB-INF/jsp/noSuchFlight.jsp new file mode 100644 index 0000000..2a6ede9 --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/jsp/noSuchFlight.jsp @@ -0,0 +1,13 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + +No Such Flight + +<% + Exception ex = (Exception) request.getAttribute("exception"); +%> +

No such flight: <%= ex.getMessage() %> +

+Flights + + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/WEB-INF/mvc-servlet.xml b/airline/server/src/main/webapp/WEB-INF/mvc-servlet.xml new file mode 100644 index 0000000..44e82e0 --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/mvc-servlet.xml @@ -0,0 +1,33 @@ + + + + + This web application context contains a simple Spring Web MVC web application that shows flights + + + + + + + + + + + + + + + + + + noSuchFlight + + + + + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/WEB-INF/web.xml b/airline/server/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..da691a4 --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,48 @@ + + + + Spring-WS Airline Sample + + contextConfigLocation + + classpath:org/springframework/ws/samples/airline/dao/jpa/applicationContext-jpa.xml + classpath:org/springframework/ws/samples/airline/service/applicationContext.xml + classpath:org/springframework/ws/samples/airline/security/applicationContext-security.xml + classpath:org/springframework/ws/samples/airline/ws/applicationContext-ws.xml + classpath:org/springframework/ws/samples/airline/jms/applicationContext-jms.xml + + + + org.springframework.web.context.ContextLoaderListener + + + ws + org.springframework.ws.transport.http.MessageDispatcherServlet + + + ws + /services + + + ws + *.wsdl + + + mvc + org.springframework.web.servlet.DispatcherServlet + + + mvc + *.html + + + xsd + text/xml + + + index.jsp + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/WEB-INF/ws-servlet.xml b/airline/server/src/main/webapp/WEB-INF/ws-servlet.xml new file mode 100644 index 0000000..df46c1f --- /dev/null +++ b/airline/server/src/main/webapp/WEB-INF/ws-servlet.xml @@ -0,0 +1,20 @@ + + + + + + + + + Builds a WSDL from the airline.xsd.This bean definition represents the airline.wsdl file found + in the root of the web application. + + + + + + + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/index.jsp b/airline/server/src/main/webapp/index.jsp new file mode 100644 index 0000000..0937555 --- /dev/null +++ b/airline/server/src/main/webapp/index.jsp @@ -0,0 +1,3 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + \ No newline at end of file diff --git a/airline/server/src/main/webapp/messages.xsd b/airline/server/src/main/webapp/messages.xsd new file mode 100644 index 0000000..309a03d --- /dev/null +++ b/airline/server/src/main/webapp/messages.xsd @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/airline/server/src/main/webapp/types.xsd b/airline/server/src/main/webapp/types.xsd new file mode 100644 index 0000000..d51193f --- /dev/null +++ b/airline/server/src/main/webapp/types.xsd @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaFlightDaoTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaFlightDaoTest.java new file mode 100644 index 0000000..3637cdf --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaFlightDaoTest.java @@ -0,0 +1,124 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import java.util.List; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.ws.samples.airline.dao.FlightDao; +import org.springframework.ws.samples.airline.domain.Airport; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.ServiceClass; + +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("applicationContext-jpa.xml") +@Transactional +public class JpaFlightDaoTest { + + private DateTime departureTime; + + private DateTime arrivalTime; + + private Interval interval; + + private Airport fromAirport; + + private Airport toAirport; + + @Autowired + private FlightDao flightDao; + + private JdbcTemplate jdbcTemplate; + + @Autowired + public void setDataSource(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @BeforeTransaction + public void createTestData() { + departureTime = new DateTime(2006, 1, 31, 10, 5, 0, 0); + arrivalTime = new DateTime(2006, 1, 31, 12, 25, 0, 0); + interval = departureTime.toLocalDate().toInterval(); + fromAirport = new Airport("RTM", "Rotterdam Airport", "Rotterdam"); + toAirport = new Airport("OSL", "Gardermoen", "Oslo"); + } + + @Before + public void insertTestData() { + jdbcTemplate.update("INSERT INTO AIRPORT(CODE, NAME, CITY) VALUES('RTM', 'Rotterdam Airport', 'Rotterdam')"); + jdbcTemplate.update("INSERT INTO AIRPORT(CODE, NAME, CITY) VALUES('OSL', 'Gardermoen', 'Oslo')"); + } + + @Test + public void getFlightsInPeriod() throws Exception { + jdbcTemplate + .update("INSERT INTO FLIGHT(NUMBER, DEPARTURE_TIME, FROM_AIRPORT_CODE, ARRIVAL_TIME, TO_AIRPORT_CODE, SERVICE_CLASS, SEATS_AVAILABLE, MILES) " + + "VALUES ('KL020','2006-01-31 10:05:00', 'RTM', '2006-01-31 12:25:00', 'OSL', 'BUSINESS', 90, 10)"); + List flights = flightDao.findFlights("RTM", "OSL", interval, ServiceClass.BUSINESS); + assertNotNull("Invalid result", flights); + assertEquals("Invalid amount of flights", 1, flights.size()); + } + + @Test + public void getFlightsOutOfPeriod() throws Exception { + jdbcTemplate + .update("INSERT INTO FLIGHT(NUMBER, DEPARTURE_TIME, FROM_AIRPORT_CODE, ARRIVAL_TIME, TO_AIRPORT_CODE, SERVICE_CLASS, SEATS_AVAILABLE, MILES) " + + "VALUES ('KL020','2006-01-31 10:05:00', 'RTM', '2006-01-31 12:25:00', 'OSL', 'BUSINESS', 90, 10)"); + DateTime dateTime = new DateTime(2006, 6, 1, 0, 0, 0, 0); + List flights = flightDao.findFlights("RTM", "OSL", new Interval(dateTime, dateTime), ServiceClass.BUSINESS); + assertNotNull("Invalid result", flights); + assertEquals("Invalid amount of flights", 0, flights.size()); + } + + @Test + public void getFlightByNumberDepartureTime() throws Exception { + jdbcTemplate + .update("INSERT INTO FLIGHT(NUMBER, DEPARTURE_TIME, FROM_AIRPORT_CODE, ARRIVAL_TIME, TO_AIRPORT_CODE, SERVICE_CLASS, SEATS_AVAILABLE, MILES) " + + "VALUES ('KL020','2006-01-31 10:05:00', 'RTM', '2006-01-31 12:25:00', 'OSL', 'BUSINESS', 90, 10)"); + Flight flight = flightDao.getFlight("KL020", departureTime); + assertNotNull("No flight returned", flight); + assertNotNull("Invalid flight id", flight.getId()); + assertEquals("Invalid flight number", "KL020", flight.getNumber()); + assertEquals("Invalid flight departure time", departureTime, flight.getDepartureTime()); + assertEquals("Invalid flight arrival time", arrivalTime, flight.getArrivalTime()); + assertEquals("Invalid flight from airport", fromAirport, flight.getFrom()); + assertEquals("Invalid flight to airport", toAirport, flight.getTo()); + assertEquals("Invalid flight service class", ServiceClass.BUSINESS, flight.getServiceClass()); + } + + @Test + public void noSuchFlight() { + Flight flight = flightDao.getFlight("INVALID", departureTime); + assertNull("Flight returned", flight); + } + +} \ No newline at end of file diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaFrequentFlyerDaoTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaFrequentFlyerDaoTest.java new file mode 100644 index 0000000..b6e20c5 --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaFrequentFlyerDaoTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("applicationContext-jpa.xml") +@Transactional +public class JpaFrequentFlyerDaoTest { + + @Autowired + private JpaFrequentFlyerDao frequentFlyerDao; + + private JdbcTemplate jdbcTemplate; + + @Autowired + public void setDataSource(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Before + public void insertTestData() { + jdbcTemplate + .update("INSERT INTO PASSENGER(ID, FIRST_NAME, LAST_NAME) " + "VALUES (42, 'Arjen', 'Poutsma')"); + jdbcTemplate + .update("INSERT INTO FREQUENT_FLYER(PASSENGER_ID, USERNAME, PASSWORD, MILES) " + + "VALUES (42, 'arjen', 'changeme', 0)"); + } + + @Test + public void getByUsername() throws Exception { + FrequentFlyer flyer = frequentFlyerDao.get("arjen"); + assertNotNull("No frequent flyer returned", flyer); + assertEquals("Invalid username", "arjen", flyer.getUsername()); + assertEquals("Invalid password", "changeme", flyer.getPassword()); + assertEquals("Invalid first name", "Arjen", flyer.getFirstName()); + assertEquals("Invalid last name", "Poutsma", flyer.getLastName()); + } + + @Test + public void noSuchUsername() { + FrequentFlyer flyer = frequentFlyerDao.get("invalid"); + assertNull("FrequentFlyer returned", flyer); + } +} \ No newline at end of file diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaTicketDaoTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaTicketDaoTest.java new file mode 100644 index 0000000..2e4a5f7 --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/dao/jpa/JpaTicketDaoTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.dao.jpa; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.domain.Ticket; + +import org.joda.time.LocalDate; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("applicationContext-jpa.xml") +@Transactional +public class JpaTicketDaoTest { + + @Autowired + private JpaTicketDao ticketDao; + + @Autowired + private JpaFlightDao flightDao; + + private JdbcTemplate jdbcTemplate; + + @Autowired + public void setDataSource(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Before + public void insertTestData() { + jdbcTemplate.update("INSERT INTO AIRPORT(CODE, NAME, CITY) VALUES('RTM', 'Rotterdam Airport', 'Rotterdam')"); + jdbcTemplate.update("INSERT INTO AIRPORT(CODE, NAME, CITY) VALUES('OSL', 'Gardermoen', 'Oslo')"); + jdbcTemplate + .update("INSERT INTO FLIGHT(ID, NUMBER, DEPARTURE_TIME, FROM_AIRPORT_CODE, ARRIVAL_TIME, TO_AIRPORT_CODE, SERVICE_CLASS, SEATS_AVAILABLE, MILES) " + + "VALUES (42, 'KL020','2006-01-31 10:05:00', 'RTM', '2006-01-31 12:25:00', 'OSL', 'BUSINESS', 90, 10)"); + } + + @Test + public void save() throws Exception { + Passenger passenger = new Passenger("Arjen", "Poutsma"); + Flight flight = flightDao.getFlight(42L); + Ticket ticket = new Ticket(); + ticket.addPassenger(passenger); + ticket.setFlight(flight); + ticket.setIssueDate(new LocalDate()); + int startTicketCount = jdbcTemplate.queryForInt("SELECT COUNT(0) FROM TICKET"); + int startPassengerCount = jdbcTemplate.queryForInt("SELECT COUNT(0) FROM PASSENGER"); + ticketDao.save(ticket); + assertNotNull("No Id generated", ticket.getId()); + int endTicketCount = jdbcTemplate.queryForInt("SELECT COUNT(0) FROM TICKET"); + int endPassengerCount = jdbcTemplate.queryForInt("SELECT COUNT(0) FROM PASSENGER"); + assertEquals("Flight not inserted", 1, endTicketCount - startTicketCount); + assertEquals("Passenger not inserted", 1, endPassengerCount - startPassengerCount); + } + +} \ No newline at end of file diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/security/SpringFrequentFlyerSecurityServiceTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/security/SpringFrequentFlyerSecurityServiceTest.java new file mode 100644 index 0000000..6cbf555 --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/security/SpringFrequentFlyerSecurityServiceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2005-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.security; + +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.ws.samples.airline.dao.FrequentFlyerDao; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; + +import junit.framework.TestCase; + +import static org.easymock.EasyMock.*; + +public class SpringFrequentFlyerSecurityServiceTest extends TestCase { + + private SpringFrequentFlyerSecurityService securityService; + + private FrequentFlyerDao flyerDaoMock; + + @Override + protected void setUp() throws Exception { + flyerDaoMock = createMock(FrequentFlyerDao.class); + securityService = new SpringFrequentFlyerSecurityService(flyerDaoMock); + } + + public void testGetCurrentlyAuthenticatedFrequentFlyer() throws Exception { + FrequentFlyer frequentFlyer = new FrequentFlyer("john"); + FrequentFlyerDetails detail = new FrequentFlyerDetails(frequentFlyer); + TestingAuthenticationToken token = new TestingAuthenticationToken(detail, null); + SecurityContext context = new SecurityContextImpl(); + context.setAuthentication(token); + SecurityContextHolder.setContext(context); + replay(flyerDaoMock); + FrequentFlyer result = securityService.getCurrentlyAuthenticatedFrequentFlyer(); + assertEquals("Invalid result", frequentFlyer, result); + verify(flyerDaoMock); + } + + public void testGetFrequentFlyer() throws Exception { + FrequentFlyer frequentFlyer = new FrequentFlyer("john"); + expect(flyerDaoMock.get("john")).andReturn(frequentFlyer); + replay(flyerDaoMock); + FrequentFlyer result = securityService.getFrequentFlyer("john"); + assertEquals("Invalid result", frequentFlyer, result); + verify(flyerDaoMock); + } +} diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/service/impl/AirlineServiceImplTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/service/impl/AirlineServiceImplTest.java new file mode 100644 index 0000000..db5cb71 --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/service/impl/AirlineServiceImplTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.service.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; +import static org.easymock.EasyMock.*; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.springframework.ws.samples.airline.dao.FlightDao; +import org.springframework.ws.samples.airline.dao.TicketDao; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.domain.ServiceClass; +import org.springframework.ws.samples.airline.domain.Ticket; +import org.springframework.ws.samples.airline.security.FrequentFlyerSecurityService; +import org.springframework.ws.samples.airline.service.NoSeatAvailableException; +import org.springframework.ws.samples.airline.service.NoSuchFlightException; + +public class AirlineServiceImplTest extends TestCase { + + private AirlineServiceImpl airlineService; + + private FlightDao flightDaoMock; + + private TicketDao ticketDaoMock; + + private FrequentFlyerSecurityService securityServiceMock; + + private String flightNumber; + + @Override + protected void setUp() throws Exception { + flightDaoMock = createMock(FlightDao.class); + ticketDaoMock = createMock(TicketDao.class); + airlineService = new AirlineServiceImpl(flightDaoMock, ticketDaoMock); + securityServiceMock = createMock(FrequentFlyerSecurityService.class); + airlineService.setFrequentFlyerSecurityService(securityServiceMock); + flightNumber = "AB1234"; + } + + public void testBookFlight() throws Exception { + DateTime departureTime = new DateTime(); + Passenger passenger = new Passenger("John", "Doe"); + List passengers = new ArrayList(); + passengers.add(passenger); + Flight flight = new Flight(); + flight.setNumber(flightNumber); + flight.setSeatsAvailable(10); + expect(flightDaoMock.getFlight(flightNumber, departureTime)).andReturn(flight); + expect(flightDaoMock.update(flight)).andReturn(flight); + Ticket ticket = new Ticket(); + ticket.setFlight(flight); + ticketDaoMock.save(isA(Ticket.class)); + + replay(flightDaoMock, ticketDaoMock, securityServiceMock); + + ticket = airlineService.bookFlight(flightNumber, departureTime, passengers); + assertNotNull("Invalid ticket", ticket); + assertEquals("Invalid flight", flight, ticket.getFlight()); + assertEquals("Invalid seats available", 9, flight.getSeatsAvailable()); + assertEquals("Invalid passengers count", 1, ticket.getPassengers().size()); + + verify(flightDaoMock, ticketDaoMock, securityServiceMock); + } + + public void testBookFlightFrequentFlyer() throws Exception { + DateTime departureTime = new DateTime(); + FrequentFlyer frequentFlyer = new FrequentFlyer("John", "Doe", "john", "changeme"); + List passengers = new ArrayList(); + passengers.add(frequentFlyer); + Flight flight = new Flight(); + flight.setNumber(flightNumber); + flight.setSeatsAvailable(1); + flight.setMiles(10); + expect(securityServiceMock.getFrequentFlyer("john")).andReturn(frequentFlyer); + expect(flightDaoMock.getFlight(flightNumber, departureTime)).andReturn(flight); + expect(flightDaoMock.update(flight)).andReturn(flight); + ticketDaoMock.save(isA(Ticket.class)); + + replay(flightDaoMock, ticketDaoMock, securityServiceMock); + + Ticket ticket = airlineService.bookFlight(flightNumber, departureTime, passengers); + assertNotNull("Invalid ticket", ticket); + assertEquals("Invalid flight", flight, ticket.getFlight()); + assertEquals("Invalid amount of miles", 10, frequentFlyer.getMiles()); + + verify(flightDaoMock, ticketDaoMock, securityServiceMock); + } + + public void testBookFlightNoSeatAvailable() throws Exception { + DateTime departureTime = new DateTime(); + List passengers = Collections.singletonList(new Passenger()); + Flight flight = new Flight(); + flight.setNumber("AB1234"); + flight.setDepartureTime(new DateTime()); + expect(flightDaoMock.getFlight(flightNumber, departureTime)).andReturn(flight); + + replay(flightDaoMock, ticketDaoMock, securityServiceMock); + + try { + airlineService.bookFlight(flightNumber, departureTime, passengers); + fail("Should have thrown an NoSeatAvailableException"); + } + catch (NoSeatAvailableException ex) { + } + + verify(flightDaoMock, ticketDaoMock, securityServiceMock); + } + + public void testBookFlightNoSuchFlight() throws Exception { + String flightNumber = "AB1234"; + DateTime departureTime = new DateTime(); + List passengers = Collections.singletonList(new Passenger()); + expect(flightDaoMock.getFlight(flightNumber, departureTime)).andReturn(null); + + replay(flightDaoMock, ticketDaoMock, securityServiceMock); + try { + airlineService.bookFlight(flightNumber, departureTime, passengers); + fail("Should have thrown an NoSuchFlightException"); + } + catch (NoSuchFlightException ex) { + } + + verify(flightDaoMock, ticketDaoMock, securityServiceMock); + } + + public void testGetFlights() throws Exception { + LocalDate departureDate = new LocalDate(2006, 1, 31); + Flight flight = new Flight(); + List flights = new ArrayList(); + flights.add(flight); + String toCode = "to"; + String fromCode = "from"; + + expect(flightDaoMock.findFlights(fromCode, toCode, departureDate.toInterval(), ServiceClass.ECONOMY)) + .andReturn(flights); + + replay(flightDaoMock, ticketDaoMock, securityServiceMock); + + List result = airlineService.getFlights(fromCode, toCode, departureDate, ServiceClass.ECONOMY); + assertEquals("Invalid result", flights, result); + + verify(flightDaoMock, ticketDaoMock, securityServiceMock); + } + + public void testGetFlightsDefaultServiceClass() throws Exception { + LocalDate departureDate = new LocalDate(2006, 1, 31); + Flight flight = new Flight(); + List flights = new ArrayList(); + flights.add(flight); + String toCode = "to"; + String fromCode = "from"; + + expect(flightDaoMock.findFlights(fromCode, toCode, departureDate.toInterval(), ServiceClass.ECONOMY)) + .andReturn(flights); + + replay(flightDaoMock, ticketDaoMock, securityServiceMock); + + List result = airlineService.getFlights(fromCode, toCode, departureDate, null); + assertEquals("Invalid result", flights, result); + + verify(flightDaoMock, ticketDaoMock, securityServiceMock); + + } +} diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/web/FlightsControllerTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/web/FlightsControllerTest.java new file mode 100644 index 0000000..b527afd --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/web/FlightsControllerTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.web; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; +import static org.easymock.EasyMock.*; +import org.joda.time.LocalDate; + +import org.springframework.ui.ModelMap; +import org.springframework.ws.samples.airline.domain.Flight; +import org.springframework.ws.samples.airline.domain.ServiceClass; +import org.springframework.ws.samples.airline.service.AirlineService; + +public class FlightsControllerTest extends TestCase { + + private FlightsController flightsController; + + private AirlineService airlineServiceMock; + + @Override + protected void setUp() throws Exception { + airlineServiceMock = createMock(AirlineService.class); + flightsController = new FlightsController(airlineServiceMock); + } + + public void testFlightList() throws Exception { + String from = "AMS"; + String to = "VCE"; + LocalDate departureDate = new LocalDate(); + ServiceClass serviceClass = ServiceClass.FIRST; + List flights = new ArrayList(); + flights.add(new Flight()); + expect(airlineServiceMock.getFlights(from, to, departureDate, serviceClass)).andReturn(flights); + + replay(airlineServiceMock); + + ModelMap model = new ModelMap(); + String view = flightsController + .flightList(from, to, departureDate.toString(), serviceClass.toString(), model); + assertNotNull("No view returned", view); + assertEquals("Invalid view name", "flights", view); + assertTrue("No flights in ModelAndView", model.containsAttribute("flights")); + verify(airlineServiceMock); + } +} \ No newline at end of file diff --git a/airline/server/src/test/java/org/springframework/ws/samples/airline/ws/AirlineEndpointTest.java b/airline/server/src/test/java/org/springframework/ws/samples/airline/ws/AirlineEndpointTest.java new file mode 100644 index 0000000..870c443 --- /dev/null +++ b/airline/server/src/test/java/org/springframework/ws/samples/airline/ws/AirlineEndpointTest.java @@ -0,0 +1,207 @@ +/* + * Copyright 2005-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.samples.airline.ws; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.DatatypeFactory; + +import org.springframework.ws.samples.airline.domain.Airport; +import org.springframework.ws.samples.airline.domain.FrequentFlyer; +import org.springframework.ws.samples.airline.domain.Passenger; +import org.springframework.ws.samples.airline.schema.BookFlightRequest; +import org.springframework.ws.samples.airline.schema.Flight; +import org.springframework.ws.samples.airline.schema.GetFlightsResponse; +import org.springframework.ws.samples.airline.schema.Name; +import org.springframework.ws.samples.airline.schema.ObjectFactory; +import org.springframework.ws.samples.airline.schema.ServiceClass; +import org.springframework.ws.samples.airline.schema.Ticket; +import org.springframework.ws.samples.airline.service.AirlineService; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDate; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Element; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.assertNotNull; + +public class AirlineEndpointTest { + + private AirlineEndpoint endpoint; + + private AirlineService airlineServiceMock; + + private DatatypeFactory datatypeFactory; + + private ObjectFactory objectFactory; + + @Before + public void setUp() throws Exception { + airlineServiceMock = createMock(AirlineService.class); + endpoint = new AirlineEndpoint(airlineServiceMock); + datatypeFactory = DatatypeFactory.newInstance(); + objectFactory = new ObjectFactory(); + } + + @Test + public void testGetFlights() throws Exception { + org.springframework.ws.samples.airline.domain.Flight domainFlight = createDomainFlight(); + + expect(airlineServiceMock.getFlights("ABC", "DEF", new LocalDate(2007, 6, 13), + org.springframework.ws.samples.airline.domain.ServiceClass.FIRST)) + .andReturn(Collections.singletonList(domainFlight)); + + replay(airlineServiceMock); + + GetFlightsResponse response = endpoint.getFlights("ABC", "DEF", "2007-06-13", "first"); + Assert.assertEquals("Invalid amount of flights received", 1, response.getFlight().size()); + Flight schemaFlight = response.getFlight().get(0); + verifySchemaFlight(schemaFlight); + + verify(airlineServiceMock); + } + + private void verifySchemaFlight(Flight schemaFlight) { + Assert.assertEquals("Invalid number", "ABC1234", schemaFlight.getNumber()); + Assert.assertEquals("Invalid departure time", + datatypeFactory.newXMLGregorianCalendar(2007, 6, 13, 12, 0, 0, 0, 0), schemaFlight.getDepartureTime()); + Assert.assertEquals("Invalid from code", "ABC", schemaFlight.getFrom().getCode()); + Assert.assertEquals("Invalid from name", "ABC Airport", schemaFlight.getFrom().getName()); + Assert.assertEquals("Invalid from city", "ABC City", schemaFlight.getFrom().getCity()); + Assert.assertEquals("Invalid arrival time", + datatypeFactory.newXMLGregorianCalendar(2007, 6, 13, 14, 0, 0, 0, 0), schemaFlight.getArrivalTime()); + Assert.assertEquals("Invalid to code", "DEF", schemaFlight.getTo().getCode()); + Assert.assertEquals("Invalid to name", "DEF Airport", schemaFlight.getTo().getName()); + Assert.assertEquals("Invalid to city", "DEF City", schemaFlight.getTo().getCity()); + Assert.assertEquals("Invalid service class", ServiceClass.FIRST, schemaFlight.getServiceClass()); + } + + private org.springframework.ws.samples.airline.domain.Flight createDomainFlight() { + org.springframework.ws.samples.airline.domain.Flight domainFlight = + new org.springframework.ws.samples.airline.domain.Flight(); + domainFlight.setNumber("ABC1234"); + domainFlight.setDepartureTime(new DateTime(2007, 6, 13, 12, 0, 0, 0, DateTimeZone.UTC)); + domainFlight.setFrom(new Airport("ABC", "ABC Airport", "ABC City")); + domainFlight.setArrivalTime(new DateTime(2007, 6, 13, 14, 0, 0, 0, DateTimeZone.UTC)); + domainFlight.setTo(new Airport("DEF", "DEF Airport", "DEF City")); + domainFlight.setServiceClass(org.springframework.ws.samples.airline.domain.ServiceClass.FIRST); + return domainFlight; + } + + @Test + public void bookFlightPassenger() throws Exception { + BookFlightRequest request = objectFactory.createBookFlightRequest(); + request.setDepartureTime(datatypeFactory.newXMLGregorianCalendar(2007, 6, 13, 12, 0, 0, 0, 0)); + request.setFlightNumber("ABC1234"); + Name passengerName = new Name(); + passengerName.setFirst("John"); + passengerName.setLast("Doe"); + BookFlightRequest.Passengers passengers = new BookFlightRequest.Passengers(); + passengers.getPassengerOrUsername().add(passengerName); + request.setPassengers(passengers); + + Passenger domainPassenger = new Passenger("John", "Doe"); + + org.springframework.ws.samples.airline.domain.Ticket domainTicket = + new org.springframework.ws.samples.airline.domain.Ticket(42L); + domainTicket.setFlight(createDomainFlight()); + domainTicket.setIssueDate(new LocalDate(2007, 6, 13)); + domainTicket.setPassengers(Collections.singleton(domainPassenger)); + + expect(airlineServiceMock.bookFlight("ABC1234", new DateTime(2007, 6, 13, 12, 0, 0, 0, DateTimeZone.UTC), + Collections.singletonList(domainPassenger))).andReturn(domainTicket); + + replay(airlineServiceMock); + + JAXBElement response = endpoint.bookFlight(request); + Ticket schemaTicket = response.getValue(); + Assert.assertEquals("Invalid id", 42L, schemaTicket.getId()); + Assert.assertEquals("Invalid issue date", + datatypeFactory.newXMLGregorianCalendarDate(2007, 6, 13, DatatypeConstants.FIELD_UNDEFINED), + schemaTicket.getIssueDate()); + Assert.assertEquals("Invalid amount of passengers", 1, schemaTicket.getPassengers().getPassenger().size()); + Name schemaPassenger = schemaTicket.getPassengers().getPassenger().get(0); + Assert.assertEquals("Invalid passenger first name", "John", schemaPassenger.getFirst()); + Assert.assertEquals("Invalid passenger first name", "Doe", schemaPassenger.getLast()); + verifySchemaFlight(schemaTicket.getFlight()); + + verify(airlineServiceMock); + } + + @Test + public void testBookFlightFrequentFlyer() throws Exception { + BookFlightRequest request = objectFactory.createBookFlightRequest(); + request.setDepartureTime(datatypeFactory.newXMLGregorianCalendar(2007, 6, 13, 12, 0, 0, 0, 0)); + request.setFlightNumber("ABC1234"); + BookFlightRequest.Passengers passengers = new BookFlightRequest.Passengers(); + passengers.getPassengerOrUsername().add("john"); + request.setPassengers(passengers); + + FrequentFlyer domainFrequentFlyer = new FrequentFlyer("John", "Doe", "john", "changeme"); + Set domainPassengers = new HashSet(); + domainPassengers.add(domainFrequentFlyer); + + org.springframework.ws.samples.airline.domain.Ticket domainTicket = + new org.springframework.ws.samples.airline.domain.Ticket(42L); + domainTicket.setFlight(createDomainFlight()); + domainTicket.setIssueDate(new LocalDate(2007, 6, 13)); + domainTicket.setPassengers(domainPassengers); + + List domainPassengerList = new ArrayList(domainPassengers); + expect(airlineServiceMock + .bookFlight("ABC1234", new DateTime(2007, 6, 13, 12, 0, 0, 0, DateTimeZone.UTC), domainPassengerList)) + .andReturn(domainTicket); + + replay(airlineServiceMock); + + JAXBElement response = endpoint.bookFlight(request); + Ticket schemaTicket = response.getValue(); + Assert.assertEquals("Invalid id", 42L, schemaTicket.getId()); + Assert.assertEquals("Invalid issue date", + datatypeFactory.newXMLGregorianCalendarDate(2007, 6, 13, DatatypeConstants.FIELD_UNDEFINED), + schemaTicket.getIssueDate()); + Assert.assertEquals("Invalid amount of passengers", 1, schemaTicket.getPassengers().getPassenger().size()); + Name schemaPassenger = schemaTicket.getPassengers().getPassenger().get(0); + Assert.assertEquals("Invalid passenger first name", "John", schemaPassenger.getFirst()); + Assert.assertEquals("Invalid passenger first name", "Doe", schemaPassenger.getLast()); + verifySchemaFlight(schemaTicket.getFlight()); + + verify(airlineServiceMock); + } + + @Test + public void testGetFrequentFlyerMileage() throws Exception { + expect(airlineServiceMock.getFrequentFlyerMileage()).andReturn(42); + + replay(airlineServiceMock); + + Element response = endpoint.getFrequentFlyerMileage(); + assertNotNull("Invalid response", response); + + verify(airlineServiceMock); + } + +} \ No newline at end of file diff --git a/airline/settings.gradle b/airline/settings.gradle new file mode 100644 index 0000000..aaeb3fd --- /dev/null +++ b/airline/settings.gradle @@ -0,0 +1,2 @@ + +include "server", "client:axis1", "client:jax-ws", "client:jms", "client:saaj", "client:spring-ws"