Merge remote-tracking branch 'iluwatar/master'

This commit is contained in:
gwildor28 2016-03-29 14:19:01 +01:00
commit 187658cce9
30 changed files with 1366 additions and 270 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,45 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" always-add-relationships="false" generalizations="true" realizations="true" <class-diagram version="1.1.8" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true"> associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.Customer" project="dao" <class id="1" language="java" name="com.iluwatar.dao.App" project="dao"
file="/dao/src/main/java/com/iluwatar/Customer.java" binary="false" corner="BOTTOM_RIGHT"> file="/dao/src/main/java/com/iluwatar/dao/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="176" y="337"/> <position height="-1" width="-1" x="910" y="191"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="false" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="false" static="true"/>
</display> </display>
</class> </class>
<class id="2" language="java" name="com.iluwatar.CustomerDaoImpl" project="dao" <interface id="2" language="java" name="com.iluwatar.dao.CustomerDao" project="dao"
file="/dao/src/main/java/com/iluwatar/CustomerDaoImpl.java" binary="false" corner="BOTTOM_RIGHT"> file="/dao/src/main/java/com/iluwatar/dao/CustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="540" y="334"/> <position height="-1" width="-1" x="536" y="187"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="3" language="java" name="com.iluwatar.CustomerDao" project="dao"
file="/dao/src/main/java/com/iluwatar/CustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="536" y="131"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display> </display>
</interface> </interface>
<realization id="4"> <class id="3" language="java" name="com.iluwatar.dao.InMemoryCustomerDao" project="dao"
<end type="SOURCE" refId="2"/> file="/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
<end type="TARGET" refId="3"/> <position height="-1" width="-1" x="289" y="455"/>
</realization> <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
<association id="5"> sort-features="false" accessors="true" visibility="true">
<end type="SOURCE" refId="2" navigable="false"> <attributes public="true" package="true" protected="true" private="false" static="true"/>
<attribute id="6" name="customers"/> <operations public="true" package="true" protected="true" private="true" static="true"/>
<multiplicity id="7" minimum="0" maximum="2147483647"/> </display>
</class>
<class id="4" language="java" name="com.iluwatar.dao.DBCustomerDao" project="dao"
file="/dao/src/main/java/com/iluwatar/dao/DBCustomerDao.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="751" y="456"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="false" static="true"/>
<operations public="true" package="true" protected="true" private="false" static="true"/>
</display>
</class>
<interface id="5" language="java" name="javax.sql.DataSource" project="dao"
file="/opt/Softwares/Eclipses/MARS/eclipse/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1083" y="459"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<association id="6">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="7" name="dataSource"/>
<multiplicity id="8" minimum="0" maximum="1"/>
</end> </end>
<end type="TARGET" refId="1" navigable="true"/> <end type="TARGET" refId="5" navigable="true"/>
<display labels="true" multiplicity="true"/> <display labels="true" multiplicity="true"/>
</association> </association>
<realization id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="2"/>
</realization>
<dependency id="10">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
</dependency>
<realization id="11">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="2"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true"> sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>

View File

@ -44,6 +44,18 @@
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>de.bechte.junit</groupId>
<artifactId>junit-hierarchicalcontextrunner</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -20,49 +20,101 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.dao; package com.iluwatar.dao;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.h2.jdbcx.JdbcDataSource;
/** /**
*
* Data Access Object (DAO) is an object that provides an abstract interface to some type of * Data Access Object (DAO) is an object that provides an abstract interface to some type of
* database or other persistence mechanism. By mapping application calls to the persistence layer, * database or other persistence mechanism. By mapping application calls to the persistence layer,
* DAO provide some specific data operations without exposing details of the database. This * DAO provide some specific data operations without exposing details of the database. This
* isolation supports the Single responsibility principle. It separates what data accesses the * isolation supports the Single responsibility principle. It separates what data accesses the
* application needs, in terms of domain-specific objects and data types (the public interface of * application needs, in terms of domain-specific objects and data types (the public interface of
* the DAO), from how these needs can be satisfied with a specific DBMS. * the DAO), from how these needs can be satisfied with a specific DBMS.
* <p> *
* With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without * <p>With the DAO pattern, we can use various method calls to retrieve/add/delete/update data
* directly interacting with the data. The below example demonstrates basic CRUD operations: select, * without directly interacting with the data source. The below example demonstrates basic CRUD
* add, update, and delete. * operations: select, add, update, and delete.
*
* *
*/ */
public class App { public class App {
private static final String DB_URL = "jdbc:h2:~/dao:customerdb";
private static Logger log = Logger.getLogger(App.class); private static Logger log = Logger.getLogger(App.class);
/** /**
* Program entry point. * Program entry point.
* *
* @param args command line args. * @param args command line args.
* @throws Exception if any error occurs.
*/ */
public static void main(final String[] args) { public static void main(final String[] args) throws Exception {
final CustomerDao customerDao = new CustomerDaoImpl(generateSampleCustomers()); final CustomerDao inMemoryDao = new InMemoryCustomerDao();
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); performOperationsUsing(inMemoryDao);
log.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2));
final DataSource dataSource = createDataSource();
createSchema(dataSource);
final CustomerDao dbDao = new DbCustomerDao(dataSource);
performOperationsUsing(dbDao);
deleteSchema(dataSource);
}
private static void deleteSchema(DataSource dataSource) throws SQLException {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
}
}
private static void createSchema(DataSource dataSource) throws SQLException {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
}
}
private static DataSource createDataSource() {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
return dataSource;
}
private static void performOperationsUsing(final CustomerDao customerDao) throws Exception {
addCustomers(customerDao);
log.info("customerDao.getAllCustomers(): ");
try (Stream<Customer> customerStream = customerDao.getAll()) {
customerStream.forEach((customer) -> log.info(customer));
}
log.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
final Customer customer = new Customer(4, "Dan", "Danson"); final Customer customer = new Customer(4, "Dan", "Danson");
customerDao.addCustomer(customer); customerDao.add(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); log.info("customerDao.getAllCustomers(): " + customerDao.getAll());
customer.setFirstName("Daniel"); customer.setFirstName("Daniel");
customer.setLastName("Danielson"); customer.setLastName("Danielson");
customerDao.updateCustomer(customer); customerDao.update(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); log.info("customerDao.getAllCustomers(): ");
customerDao.deleteCustomer(customer); try (Stream<Customer> customerStream = customerDao.getAll()) {
log.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); customerStream.forEach((cust) -> log.info(cust));
}
customerDao.delete(customer);
log.info("customerDao.getAllCustomers(): " + customerDao.getAll());
}
private static void addCustomers(CustomerDao customerDao) throws Exception {
for (Customer customer : generateSampleCustomers()) {
customerDao.add(customer);
}
} }
/** /**

View File

@ -20,11 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.dao; package com.iluwatar.dao;
/** /**
* * A customer POJO that represents the data that will be read from the data source.
* Customer
* *
*/ */
public class Customer { public class Customer {
@ -34,7 +34,7 @@ public class Customer {
private String lastName; private String lastName;
/** /**
* Constructor * Creates an instance of customer.
*/ */
public Customer(final int id, final String firstName, final String lastName) { public Customer(final int id, final String firstName, final String lastName) {
this.id = id; this.id = id;
@ -73,12 +73,12 @@ public class Customer {
} }
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object that) {
boolean isEqual = false; boolean isEqual = false;
if (this == o) { if (this == that) {
isEqual = true; isEqual = true;
} else if (o != null && getClass() == o.getClass()) { } else if (that != null && getClass() == that.getClass()) {
final Customer customer = (Customer) o; final Customer customer = (Customer) that;
if (getId() == customer.getId()) { if (getId() == customer.getId()) {
isEqual = true; isEqual = true;
} }

View File

@ -20,24 +20,62 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.dao; package com.iluwatar.dao;
import java.util.List; import java.util.Optional;
import java.util.stream.Stream;
/** /**
* In an application the Data Access Object (DAO) is a part of Data access layer. It is an object
* that provides an interface to some type of persistence mechanism. By mapping application calls
* to the persistence layer, DAO provides some specific data operations without exposing details
* of the database. This isolation supports the Single responsibility principle. It separates what
* data accesses the application needs, in terms of domain-specific objects and data types
* (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS,
* database schema, etc.
* *
* CustomerDao * <p>Any change in the way data is stored and retrieved will not change the client code as the
* * client will be using interface and need not worry about exact source.
*
* @see InMemoryCustomerDao
* @see DbCustomerDao
*/ */
public interface CustomerDao { public interface CustomerDao {
List<Customer> getAllCustomers(); /**
* @return all the customers as a stream. The stream may be lazily or eagerly evaluated based
* on the implementation. The stream must be closed after use.
* @throws Exception if any error occurs.
*/
Stream<Customer> getAll() throws Exception;
/**
* @param id unique identifier of the customer.
* @return an optional with customer if a customer with unique identifier <code>id</code>
* exists, empty optional otherwise.
* @throws Exception if any error occurs.
*/
Optional<Customer> getById(int id) throws Exception;
Customer getCustomerById(int id); /**
* @param customer the customer to be added.
* @return true if customer is successfully added, false if customer already exists.
* @throws Exception if any error occurs.
*/
boolean add(Customer customer) throws Exception;
void addCustomer(Customer customer); /**
* @param customer the customer to be updated.
* @return true if customer exists and is successfully updated, false otherwise.
* @throws Exception if any error occurs.
*/
boolean update(Customer customer) throws Exception;
void updateCustomer(Customer customer); /**
* @param customer the customer to be deleted.
void deleteCustomer(Customer customer); * @return true if customer exists and is successfully deleted, false otherwise.
* @throws Exception if any error occurs.
*/
boolean delete(Customer customer) throws Exception;
} }

View File

@ -1,84 +0,0 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import java.util.List;
/**
*
* The data access object (DAO) is an object that provides an abstract interface to some type of
* database or other persistence mechanism. By mapping application calls to the persistence layer,
* DAO provide some specific data operations without exposing details of the database. This
* isolation supports the Single responsibility principle. It separates what data accesses the
* application needs, in terms of domain-specific objects and data types (the public interface of
* the DAO), from how these needs can be satisfied with a specific DBMS, database schema, etc.
*
*/
public class CustomerDaoImpl implements CustomerDao {
// Represents the DB structure for our example so we don't have to managed it ourselves
// Note: Normally this would be in the form of an actual database and not part of the Dao Impl.
private List<Customer> customers;
public CustomerDaoImpl(final List<Customer> customers) {
this.customers = customers;
}
@Override
public List<Customer> getAllCustomers() {
return customers;
}
@Override
public Customer getCustomerById(final int id) {
Customer customer = null;
for (final Customer cus : getAllCustomers()) {
if (cus.getId() == id) {
customer = cus;
break;
}
}
return customer;
}
@Override
public void addCustomer(final Customer customer) {
if (getCustomerById(customer.getId()) == null) {
customers.add(customer);
}
}
@Override
public void updateCustomer(final Customer customer) {
if (getAllCustomers().contains(customer)) {
final int index = getAllCustomers().indexOf(customer);
getAllCustomers().set(index, customer);
}
}
@Override
public void deleteCustomer(final Customer customer) {
getAllCustomers().remove(customer);
}
}

View File

@ -0,0 +1,9 @@
package com.iluwatar.dao;
public interface CustomerSchemaSql {
String CREATE_SCHEMA_SQL = "CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), "
+ "LNAME VARCHAR(100))";
String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS";
}

View File

@ -0,0 +1,183 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.sql.DataSource;
/**
* An implementation of {@link CustomerDao} that persists customers in RDBMS.
*
*/
public class DbCustomerDao implements CustomerDao {
private final DataSource dataSource;
/**
* Creates an instance of {@link DbCustomerDao} which uses provided <code>dataSource</code>
* to store and retrieve customer information.
*
* @param dataSource a non-null dataSource.
*/
public DbCustomerDao(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* @return a lazily populated stream of customers. Note the stream returned must be closed to
* free all the acquired resources. The stream keeps an open connection to the database till
* it is complete or is closed manually.
*/
@Override
public Stream<Customer> getAll() throws Exception {
Connection connection;
try {
connection = getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS");
ResultSet resultSet = statement.executeQuery();
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Customer>(Long.MAX_VALUE,
Spliterator.ORDERED) {
@Override
public boolean tryAdvance(Consumer<? super Customer> action) {
try {
if (!resultSet.next()) {
return false;
}
action.accept(createCustomer(resultSet));
return true;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}, false).onClose(() -> mutedClose(connection));
} catch (SQLException e) {
throw new Exception(e.getMessage(), e);
}
}
private Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
private void mutedClose(Connection connection) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private Customer createCustomer(ResultSet resultSet) throws SQLException {
return new Customer(resultSet.getInt("ID"),
resultSet.getString("FNAME"),
resultSet.getString("LNAME"));
}
/**
* {@inheritDoc}
*/
@Override
public Optional<Customer> getById(int id) throws Exception {
try (Connection connection = getConnection();
PreparedStatement statement =
connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
statement.setInt(1, id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return Optional.of(createCustomer(resultSet));
} else {
return Optional.empty();
}
} catch (SQLException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(Customer customer) throws Exception {
if (getById(customer.getId()).isPresent()) {
return false;
}
try (Connection connection = getConnection();
PreparedStatement statement =
connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) {
statement.setInt(1, customer.getId());
statement.setString(2, customer.getFirstName());
statement.setString(3, customer.getLastName());
statement.execute();
return true;
} catch (SQLException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean update(Customer customer) throws Exception {
try (Connection connection = getConnection();
PreparedStatement statement =
connection.prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) {
statement.setString(1, customer.getFirstName());
statement.setString(2, customer.getLastName());
statement.setInt(3, customer.getId());
return statement.executeUpdate() > 0;
} catch (SQLException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean delete(Customer customer) throws Exception {
try (Connection connection = getConnection();
PreparedStatement statement =
connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
statement.setInt(1, customer.getId());
return statement.executeUpdate() > 0;
} catch (SQLException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
}

View File

@ -0,0 +1,73 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
/**
* An in memory implementation of {@link CustomerDao}, which stores the customers in JVM memory
* and data is lost when the application exits.
* <br/>
* This implementation is useful as temporary database or for testing.
*/
public class InMemoryCustomerDao implements CustomerDao {
private Map<Integer, Customer> idToCustomer = new HashMap<>();
/**
* An eagerly evaluated stream of customers stored in memory.
*/
@Override
public Stream<Customer> getAll() {
return idToCustomer.values().stream();
}
@Override
public Optional<Customer> getById(final int id) {
return Optional.ofNullable(idToCustomer.get(id));
}
@Override
public boolean add(final Customer customer) {
if (getById(customer.getId()).isPresent()) {
return false;
}
idToCustomer.put(customer.getId(), customer);
return true;
}
@Override
public boolean update(final Customer customer) {
return idToCustomer.replace(customer.getId(), customer) != null;
}
@Override
public boolean delete(final Customer customer) {
return idToCustomer.remove(customer.getId()) != null;
}
}

View File

@ -20,18 +20,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.dao; package com.iluwatar.dao;
import org.junit.Test; import org.junit.Test;
import java.io.IOException;
/** /**
* Tests that DAO example runs without errors. * Tests that DAO example runs without errors.
*/ */
public class AppTest { public class AppTest {
@Test @Test
public void test() throws IOException { public void test() throws Exception {
String[] args = {}; String[] args = {};
App.main(args); App.main(args);
} }

View File

@ -1,121 +0,0 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class CustomerDaoImplTest {
private CustomerDaoImpl impl;
private List<Customer> customers;
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
@Before
public void setUp() {
customers = new ArrayList<>();
customers.add(CUSTOMER);
impl = new CustomerDaoImpl(customers);
}
@Test
public void deleteExistingCustomer() {
assertEquals(1, impl.getAllCustomers().size());
impl.deleteCustomer(CUSTOMER);
assertTrue(impl.getAllCustomers().isEmpty());
}
@Test
public void deleteNonExistingCustomer() {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
impl.deleteCustomer(nonExistingCustomer);
assertEquals(1, impl.getAllCustomers().size());
}
@Test
public void updateExistingCustomer() {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
impl.updateCustomer(customer);
final Customer cust = impl.getCustomerById(CUSTOMER.getId());
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
@Test
public void updateNonExistingCustomer() {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
impl.updateCustomer(customer);
assertNull(impl.getCustomerById(nonExistingId));
final Customer existingCustomer = impl.getCustomerById(CUSTOMER.getId());
assertEquals(CUSTOMER.getFirstName(), existingCustomer.getFirstName());
assertEquals(CUSTOMER.getLastName(), existingCustomer.getLastName());
}
@Test
public void addCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void addAlreadyAddedCustomer() {
final Customer newCustomer = new Customer(3, "George", "Patton");
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
impl.addCustomer(newCustomer);
assertEquals(2, impl.getAllCustomers().size());
}
@Test
public void getExistinCustomerById() {
assertEquals(CUSTOMER, impl.getCustomerById(CUSTOMER.getId()));
}
@Test
public void getNonExistinCustomerById() {
final int nonExistingId = getNonExistingCustomerId();
assertNull(impl.getCustomerById(nonExistingId));
}
/**
* An arbitrary number which does not correspond to an active Customer id.
*
* @return an int of a customer id which doesn't exist
*/
private int getNonExistingCustomerId() {
return 999;
}
}

View File

@ -20,6 +20,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
package com.iluwatar.dao; package com.iluwatar.dao;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@ -0,0 +1,247 @@
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
/**
* Tests {@link DbCustomerDao}.
*/
@RunWith(HierarchicalContextRunner.class)
public class DbCustomerDaoTest {
private static final String DB_URL = "jdbc:h2:~/dao:customerdb";
private DbCustomerDao dao;
private Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
/**
* Creates customers schema.
* @throws SQLException if there is any error while creating schema.
*/
@Before
public void createSchema() throws SQLException {
try (Connection connection = DriverManager.getConnection(DB_URL);
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
}
}
/**
* Represents the scenario where DB connectivity is present.
*/
public class ConnectionSuccess {
/**
* Setup for connection success scenario.
* @throws Exception if any error occurs.
*/
@Before
public void setUp() throws Exception {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
dao = new DbCustomerDao(dataSource);
boolean result = dao.add(existingCustomer);
assertTrue(result);
}
/**
* Represents the scenario when DAO operations are being performed on a non existing customer.
*/
public class NonExistingCustomer {
@Test
public void addingShouldResultInSuccess() throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assumeTrue(allCustomers.count() == 1);
}
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.add(nonExistingCustomer);
assertTrue(result);
assertCustomerCountIs(2);
assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get());
}
@Test
public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.delete(nonExistingCustomer);
assertFalse(result);
assertCustomerCountIs(1);
}
@Test
public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
boolean result = dao.update(customer);
assertFalse(result);
assertFalse(dao.getById(nonExistingId).isPresent());
}
@Test
public void retrieveShouldReturnNoCustomer() throws Exception {
assertFalse(dao.getById(getNonExistingCustomerId()).isPresent());
}
}
/**
* Represents a scenario where DAO operations are being performed on an already existing
* customer.
*
*/
public class ExistingCustomer {
@Test
public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception {
Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
boolean result = dao.add(existingCustomer);
assertFalse(result);
assertCustomerCountIs(1);
assertEquals(existingCustomer, dao.getById(existingCustomer.getId()).get());
}
@Test
public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception {
boolean result = dao.delete(existingCustomer);
assertTrue(result);
assertCustomerCountIs(0);
assertFalse(dao.getById(existingCustomer.getId()).isPresent());
}
@Test
public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(existingCustomer.getId(), newFirstname, newLastname);
boolean result = dao.update(customer);
assertTrue(result);
final Customer cust = dao.getById(existingCustomer.getId()).get();
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
}
}
/**
* Represents a scenario where DB connectivity is not present due to network issue, or
* DB service unavailable.
*
*/
public class ConnectivityIssue {
private static final String EXCEPTION_CAUSE = "Connection not available";
@Rule public ExpectedException exception = ExpectedException.none();
/**
* setup a connection failure scenario.
* @throws SQLException if any error occurs.
*/
@Before
public void setUp() throws SQLException {
dao = new DbCustomerDao(mockedDatasource());
exception.expect(Exception.class);
exception.expectMessage(EXCEPTION_CAUSE);
}
private DataSource mockedDatasource() throws SQLException {
DataSource mockedDataSource = mock(DataSource.class);
Connection mockedConnection = mock(Connection.class);
SQLException exception = new SQLException(EXCEPTION_CAUSE);
doThrow(exception).when(mockedConnection).prepareStatement(Mockito.anyString());
doReturn(mockedConnection).when(mockedDataSource).getConnection();
return mockedDataSource;
}
@Test
public void addingACustomerFailsWithExceptionAsFeedbackToClient() throws Exception {
dao.add(new Customer(2, "Bernard", "Montgomery"));
}
@Test
public void deletingACustomerFailsWithExceptionAsFeedbackToTheClient() throws Exception {
dao.delete(existingCustomer);
}
@Test
public void updatingACustomerFailsWithFeedbackToTheClient() throws Exception {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname));
}
@Test
public void retrievingACustomerByIdFailsWithExceptionAsFeedbackToClient() throws Exception {
dao.getById(existingCustomer.getId());
}
@Test
public void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() throws Exception {
dao.getAll();
}
}
/**
* Delete customer schema for fresh setup per test.
* @throws SQLException if any error occurs.
*/
@After
public void deleteSchema() throws SQLException {
try (Connection connection = DriverManager.getConnection(DB_URL);
Statement statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
}
}
private void assertCustomerCountIs(int count) throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assertTrue(allCustomers.count() == count);
}
}
/**
* An arbitrary number which does not correspond to an active Customer id.
*
* @return an int of a customer id which doesn't exist
*/
private int getNonExistingCustomerId() {
return 999;
}
}

View File

@ -0,0 +1,164 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
/**
* Tests {@link InMemoryCustomerDao}.
*/
@RunWith(HierarchicalContextRunner.class)
public class InMemoryCustomerDaoTest {
private InMemoryCustomerDao dao;
private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger");
@Before
public void setUp() {
dao = new InMemoryCustomerDao();
assertTrue(dao.add(CUSTOMER));
}
/**
* Represents the scenario when the DAO operations are being performed on a non existent
* customer.
*/
public class NonExistingCustomer {
@Test
public void addingShouldResultInSuccess() throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assumeTrue(allCustomers.count() == 1);
}
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.add(nonExistingCustomer);
assertTrue(result);
assertCustomerCountIs(2);
assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get());
}
@Test
public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund");
boolean result = dao.delete(nonExistingCustomer);
assertFalse(result);
assertCustomerCountIs(1);
}
@Test
public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
final int nonExistingId = getNonExistingCustomerId();
final String newFirstname = "Douglas";
final String newLastname = "MacArthur";
final Customer customer = new Customer(nonExistingId, newFirstname, newLastname);
boolean result = dao.update(customer);
assertFalse(result);
assertFalse(dao.getById(nonExistingId).isPresent());
}
@Test
public void retrieveShouldReturnNoCustomer() throws Exception {
assertFalse(dao.getById(getNonExistingCustomerId()).isPresent());
}
}
/**
* Represents the scenario when the DAO operations are being performed on an already existing
* customer.
*/
public class ExistingCustomer {
@Test
public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception {
boolean result = dao.add(CUSTOMER);
assertFalse(result);
assertCustomerCountIs(1);
assertEquals(CUSTOMER, dao.getById(CUSTOMER.getId()).get());
}
@Test
public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception {
boolean result = dao.delete(CUSTOMER);
assertTrue(result);
assertCustomerCountIs(0);
assertFalse(dao.getById(CUSTOMER.getId()).isPresent());
}
@Test
public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception {
final String newFirstname = "Bernard";
final String newLastname = "Montgomery";
final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
boolean result = dao.update(customer);
assertTrue(result);
final Customer cust = dao.getById(CUSTOMER.getId()).get();
assertEquals(newFirstname, cust.getFirstName());
assertEquals(newLastname, cust.getLastName());
}
@Test
public void retriveShouldReturnTheCustomer() {
Optional<Customer> optionalCustomer = dao.getById(CUSTOMER.getId());
assertTrue(optionalCustomer.isPresent());
assertEquals(CUSTOMER, optionalCustomer.get());
}
}
/**
* An arbitrary number which does not correspond to an active Customer id.
*
* @return an int of a customer id which doesn't exist
*/
private int getNonExistingCustomerId() {
return 999;
}
private void assertCustomerCountIs(int count) throws Exception {
try (Stream<Customer> allCustomers = dao.getAll()) {
assertTrue(allCustomers.count() == count);
}
}
}

View File

@ -29,6 +29,7 @@ package com.iluwatar.decorator;
*/ */
public class Troll implements Hostile { public class Troll implements Hostile {
@Override
public void attack() { public void attack() {
System.out.println("The troll swings at you with a club!"); System.out.println("The troll swings at you with a club!");
} }
@ -38,6 +39,7 @@ public class Troll implements Hostile {
return 10; return 10;
} }
@Override
public void fleeBattle() { public void fleeBattle() {
System.out.println("The troll shrieks in horror and runs away!"); System.out.println("The troll shrieks in horror and runs away!");
} }

28
mute-idiom/README.md Normal file
View File

@ -0,0 +1,28 @@
---
layout: pattern
title: Mute Idiom
folder: mute-idiom
permalink: /patterns/mute-idiom/
categories: Other
tags:
- Java
- Difficulty-Beginner
- Idiom
---
## Intent
Provide a template to supress any exceptions that either are declared but cannot occur or should only be logged;
while executing some business logic. The template removes the need to write repeated `try-catch` blocks.
![alt text](./etc/mute-idiom.png "Mute Idiom")
## Applicability
Use this idiom when
* an API declares some exception but can never throw that exception. Eg. ByteArrayOutputStream bulk write method.
* you need to suppress some exception just by logging it, such as closing a resource.
## Credits
* [JOOQ: Mute Design Pattern](http://blog.jooq.org/2016/02/18/the-mute-design-pattern/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="true" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.mute.App" project="mute-idiom"
file="/mute-idiom/src/main/java/com/iluwatar/mute/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="168" y="234"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="false" static="true"/>
<operations public="true" package="true" protected="true" private="false" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.mute.Mute" project="mute-idiom"
file="/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java" binary="false" corner="BOTTOM_RIGHT">
<position height="115" width="203" x="70" y="375"/>
<display autosize="false" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="false" static="true"/>
</display>
</class>
<interface id="3" language="java" name="com.iluwatar.mute.CheckedRunnable" project="mute-idiom"
file="/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="407" y="432"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<interface id="4" language="java" name="com.iluwatar.mute.Resource" project="mute-idiom"
file="/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="383" y="235"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<interface id="5" language="java" name="java.io.Closeable" project="dao"
file="/opt/Softwares/Eclipses/MARS/eclipse/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="384" y="109"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<dependency id="6">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="4"/>
</dependency>
<dependency id="7">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
</dependency>
<dependency id="8">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="3"/>
</dependency>
<generalization id="9">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="5"/>
</generalization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

34
mute-idiom/pom.xml Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<!-- The MIT License Copyright (c) 2014 Ilkka Seppälä Permission is hereby
granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial portions
of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. -->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<artifactId>mute-idiom</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,103 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.mute;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.SQLException;
/**
* Mute pattern is utilized when we need to suppress an exception due to an API flaw or in
* situation when all we can do to handle the exception is to log it.
* This pattern should not be used everywhere. It is very important to logically handle the
* exceptions in a system, but some situations like the ones described above require this pattern,
* so that we don't need to repeat
* <pre>
* <code>
* try {
* // code that may throwing exception we need to ignore or may never be thrown
* } catch (Exception ex) {
* // ignore by logging or throw error if unexpected exception occurs
* }
* </code>
* </pre> every time we need to ignore an exception.
*
*/
public class App {
/**
* Program entry point.
*
* @param args command line args.
* @throws Exception if any exception occurs
*/
public static void main(String[] args) throws Exception {
useOfLoggedMute();
useOfMute();
}
/*
* Typically used when the API declares some exception but cannot do so. Usually a
* signature mistake.In this example out is not supposed to throw exception as it is a
* ByteArrayOutputStream. So we utilize mute, which will throw AssertionError if unexpected
* exception occurs.
*/
private static void useOfMute() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Mute.mute(() -> out.write("Hello".getBytes()));
}
private static void useOfLoggedMute() throws SQLException {
Resource resource = null;
try {
resource = acquireResource();
utilizeResource(resource);
} finally {
closeResource(resource);
}
}
/*
* All we can do while failed close of a resource is to log it.
*/
private static void closeResource(Resource resource) {
Mute.loggedMute(() -> resource.close());
}
private static void utilizeResource(Resource resource) throws SQLException {
System.out.println("Utilizing acquired resource: " + resource);
}
private static Resource acquireResource() throws SQLException {
return new Resource() {
@Override
public void close() throws IOException {
throw new IOException("Error in closing resource: " + this);
}
};
}
}

View File

@ -0,0 +1,37 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.mute;
/**
* A runnable which may throw exception on execution.
*
*/
@FunctionalInterface
public interface CheckedRunnable {
/**
* Same as {@link Runnable#run()} with a possibility of exception in execution.
* @throws Exception if any exception occurs.
*/
void run() throws Exception;
}

View File

@ -0,0 +1,68 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.mute;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* A utility class that allows you to utilize mute idiom.
*/
public final class Mute {
// The constructor is never meant to be called.
private Mute() {}
/**
* Executes the <code>runnable</code> and throws the exception occurred within a {@link AssertionError}.
* This method should be utilized to mute the operations that are guaranteed not to throw an exception.
* For instance {@link ByteArrayOutputStream#write(byte[])} declares in it's signature that it can throw
* an {@link IOException}, but in reality it cannot. This is because the bulk write method is not overridden
* in {@link ByteArrayOutputStream}.
*
* @param runnable a runnable that should never throw an exception on execution.
*/
public static void mute(CheckedRunnable runnable) {
try {
runnable.run();
} catch (Exception e) {
throw new AssertionError(e);
}
}
/**
* Executes the <code>runnable</code> and logs the exception occurred on {@link System#err}.
* This method should be utilized to mute the operations about which most you can do is log.
* For instance while closing a connection to database, or cleaning up a resource,
* all you can do is log the exception occurred.
*
* @param runnable a runnable that may throw an exception on execution.
*/
public static void loggedMute(CheckedRunnable runnable) {
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,35 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.mute;
import java.io.Closeable;
/**
* Represents any resource that the application might acquire and that must be closed
* after it is utilized. Example of such resources can be a database connection, open
* files, sockets.
*/
public interface Resource extends Closeable {
}

View File

@ -0,0 +1,38 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.mute;
import org.junit.Test;
/**
* Tests that Mute idiom example runs without errors.
*
*/
public class AppTest {
@Test
public void test() throws Exception {
App.main(null);
}
}

View File

@ -0,0 +1,78 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.mute;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class MuteTest {
private static final String MESSAGE = "should not occur";
@Rule public ExpectedException exception = ExpectedException.none();
@Test
public void muteShouldRunTheCheckedRunnableAndNotThrowAnyExceptionIfCheckedRunnableDoesNotThrowAnyException() {
Mute.mute(() -> methodNotThrowingAnyException());
}
@Test
public void muteShouldRethrowUnexpectedExceptionAsAssertionError() throws Exception {
exception.expect(AssertionError.class);
exception.expectMessage(MESSAGE);
Mute.mute(() -> methodThrowingException());
}
@Test
public void loggedMuteShouldRunTheCheckedRunnableAndNotThrowAnyExceptionIfCheckedRunnableDoesNotThrowAnyException() {
Mute.loggedMute(() -> methodNotThrowingAnyException());
}
@Test
public void loggedMuteShouldLogExceptionTraceBeforeSwallowingIt() throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
System.setErr(new PrintStream(stream));
Mute.loggedMute(() -> methodThrowingException());
assertTrue(new String(stream.toByteArray()).contains(MESSAGE));
}
private void methodNotThrowingAnyException() {
System.out.println("Executed successfully");
}
private void methodThrowingException() throws Exception {
throw new Exception(MESSAGE);
}
}

View File

@ -100,6 +100,8 @@ public class App {
System.out.println(person); System.out.println(person);
} }
repository.deleteAll();
context.close(); context.close();
} }

View File

@ -54,7 +54,8 @@
<property name="jpaProperties"> <property name="jpaProperties">
<map> <map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <entry key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<entry key="hibernate.hbm2ddl.auto" value="create-drop" /> <entry key="hibernate.hbm2ddl.auto" value="create" />
<entry key="hibernate.show_sql" value="false" />
</map> </map>
</property> </property>
</bean> </bean>

View File

@ -31,6 +31,6 @@ public class MeleeStrategy implements DragonSlayingStrategy {
@Override @Override
public void execute() { public void execute() {
System.out.println("With your Excalibur you severe the dragon's head!"); System.out.println("With your Excalibur you sever the dragon's head!");
} }
} }

View File

@ -52,7 +52,7 @@ public class DragonSlayingStrategyTest {
return Arrays.asList( return Arrays.asList(
new Object[]{ new Object[]{
new MeleeStrategy(), new MeleeStrategy(),
"With your Excalibur you severe the dragon's head!" "With your Excalibur you sever the dragon's head!"
}, },
new Object[]{ new Object[]{
new ProjectileStrategy(), new ProjectileStrategy(),