001/*
002 * Copyright 2014 Atteo.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.atteo.moonshine.postgresql;
018
019import javax.inject.Inject;
020import javax.sql.DataSource;
021import javax.xml.bind.annotation.XmlElement;
022import javax.xml.bind.annotation.XmlIDREF;
023import javax.xml.bind.annotation.XmlRootElement;
024
025import org.atteo.moonshine.database.DatabaseService;
026import org.atteo.moonshine.jta.JtaDataSourceWrapper;
027import org.atteo.moonshine.jta.JtaService;
028import org.atteo.moonshine.jta.PoolOptions;
029import org.atteo.moonshine.services.ImportService;
030import org.postgresql.ds.PGSimpleDataSource;
031import org.postgresql.ds.common.BaseDataSource;
032import org.postgresql.xa.PGXADataSource;
033
034import com.google.inject.AbstractModule;
035import com.google.inject.Module;
036import com.google.inject.Provider;
037import com.google.inject.Scopes;
038
039/**
040 * Connects to the PostgreSQL database.
041 */
042@XmlRootElement(name = "postgresql")
043public class PostgreSQLService extends DatabaseService {
044    @ImportService
045    @XmlIDREF
046    @XmlElement
047    private JtaService jta;
048
049    /**
050     * Sets the name of the PostgreSQL database, running on the server identified by the serverName property.
051     */
052    @XmlElement(required = true)
053    private String databaseName;
054
055    /**
056     * Sets the name of the host the PostgreSQL database is running on. The default value is localhost.
057     */
058    @XmlElement
059    private String serverName;
060
061    /**
062     * Sets the port which the PostgreSQL server is listening on for TCP/IP connections.
063     */
064    @XmlElement
065    private Integer portNumber;
066
067    /**
068     * Database user.
069     */
070    @XmlElement
071    private String user = "";
072
073    /**
074     * Database password.
075     */
076    @XmlElement
077    private String password = "";
078
079    /**
080     * Connection pool options.
081     */
082    @XmlElement
083    private PoolOptions pool;
084
085    @XmlElement
086    private String testQuery = "select 1";
087
088    @Inject
089    private JtaDataSourceWrapper wrapper;
090
091    private DataSource dataSource;
092
093    private class DataSourceProvider implements Provider<DataSource> {
094        @Inject
095        private JtaDataSourceWrapper wrapper;
096
097        private void configure(BaseDataSource dataSource) {
098            dataSource.setDatabaseName(databaseName);
099            if (serverName != null) {
100                dataSource.setServerName(serverName);
101            }
102            if (portNumber != null) {
103                dataSource.setPortNumber(portNumber);
104            }
105
106            if (user != null) {
107                dataSource.setUser(user);
108            }
109
110            if (password != null) {
111                dataSource.setPassword(password);
112            }
113        }
114
115        @Override
116        public DataSource get() {
117            final PGSimpleDataSource migrationDataSource = new PGSimpleDataSource();
118            configure(migrationDataSource);
119            executeMigrations(migrationDataSource);
120
121            final PGXADataSource xaDataSource = new PGXADataSource();
122            configure(xaDataSource);
123
124            String name = "defaultDataSource";
125            if (getId() != null) {
126                name = getId();
127            }
128            dataSource = wrapper.wrap(name, xaDataSource, pool, testQuery);
129            return dataSource;
130        }
131    }
132
133    @Override
134    public Module configure() {
135        return new AbstractModule() {
136            @Override
137            protected void configure() {
138                bind(DataSource.class).toProvider(new DataSourceProvider()).in(Scopes.SINGLETON);
139            }
140        };
141    }
142
143    @Override
144    public void close() {
145        if (dataSource != null) {
146            wrapper.close(dataSource);
147        }
148    }
149}