1 package com.trendmicro.grid.acl.mssql;
2
3 import net.sf.tinyjee.config.Configuration;
4 import net.sf.tinyjee.config.ConfigurationVisitor;
5 import net.sf.tinyjee.config.Connection;
6 import net.sf.tinyjee.config.PropertySection;
7 import net.sf.tinyjee.config.documentation.PropertyDocumentation;
8 import net.sf.tinyjee.config.documentation.ResourceDocumentation;
9 import net.sf.tinyjee.config.documentation.SectionDocumentation;
10 import net.sf.tinyjee.ds.mssql.MSSQLConfigurationBuilder;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import java.util.Collection;
15
16 import static net.sf.tinyjee.config.Connection.Protocol.jdbc;
17 import static net.sf.tinyjee.config.Connection.createGroupFilter;
18 import static net.sf.tinyjee.config.Connection.createProtocolFilter;
19 import static net.sf.tinyjee.config.Connection.select;
20 import static net.sf.tinyjee.config.Names.*;
21 import static net.sf.tinyjee.ds.config.DataSourceConnection.*;
22
23
24
25
26
27
28 @ResourceDocumentation(
29 name = MSSQLConfigurator.NAME_CONNECTION,
30 description = "Defines an outing JDBC connection to the MSSQL database used to store the CoreDB schema.\n" +
31 "\n" +
32 "This connection is only active if the system property 'mssqlserver' is set to " +
33 "the server's 'hostname'. The access layer will gracefully fallback to using a local " +
34 "HSQL database if no MSSQL server is specified.\n" +
35 "\n" +
36 "Use the command line option '-Dmssqlserver=hostname' to let the access layer use " +
37 "the MSSQL server instead of its own embedded database.\n" +
38 "\n" +
39 "For high-availability use '-DmssqlserverFailover=hostname' to specify an alternate " +
40 "failover partner server to use when the primary MSSQL server is shutdown.\n" +
41 "\n" +
42 "Note: In some cases it may be desirable to harden the MSSQL host configuration.\n" +
43 "In order to achieve this, remove the 'if' attribute from the connection tag and " +
44 "replace '${mssqlserver}' inside the connection address with the hardened host value. " +
45 "After this change, setting the command line option -Dmssqlserver will no longer " +
46 "have any effect.",
47 properties = {
48 @PropertyDocumentation(property = MSSQLConfigurator.CONFIGURATION_VERSION,
49 description = "This is a revision number for the auto-configurator.\n" +
50 "Do not modify this."),
51 @PropertyDocumentation(property = MSSQLConfigurator.CONFIGURATION_USE_XA,
52 description = "This is a state information for the auto-configurator.\n" +
53 "Do not modify this.\n" +
54 "\n" +
55 "Use '-Dmssql.no.xa=true' to enable emulated-XA instead of real XA.")
56 }
57 )
58 @SectionDocumentation(
59 name = "tinyjee-jpa",
60 properties = {
61 @PropertyDocumentation(
62 property = "jpaDataSource.CoreDB",
63 description = "Sets the name of the datasource to use for accessing the GRID CoreDB schema. " +
64 "By default, an expression is used that maps the MSSQL datasource if available " +
65 "and uses the embedded HSQL database as a fallback. It is not recommended to " +
66 "change this default value."
67 )
68 }
69 )
70 public class MSSQLConfigurator implements ConfigurationVisitor, MSSQLSystemProperties {
71
72 private static final Logger log = LoggerFactory.getLogger(MSSQLConfigurator.class);
73
74 public static final String NAME_CONNECTION = "gacl-mssql";
75
76 public static final String CONFIGURATION_VERSION = "auto-configured-version";
77 public static final int CONFIGURATION_VERSION_VALUE = 2;
78
79 public static final String CONFIGURATION_USE_XA = "auto-configured-useXA";
80
81
82
83
84 public static final boolean MSSQL_NO_XA = Boolean.getBoolean(PROPERTY_NO_XA);
85
86 public static final boolean MSSQL_USE_AO = Boolean.getBoolean(PROPERTY_USE_AO);
87
88 private static final String DEFAULT_DB_NAME = "GRIDCoreDB";
89 private static final String DEFAULT_USER_NAME = "CoreDB";
90 private static final String DEFAULT_PASSWORD = "tr3ndm1cro";
91
92 @Override
93 public void visitConfiguration(Configuration configuration) {
94 configureJpaSection(configuration);
95 configureMSSQLConnections(configuration);
96 }
97
98 private void configureMSSQLConnections(Configuration configuration) {
99 final String group = MSSQLConfigurationBuilder.GROUP;
100 Collection<Connection> connections = select(configuration.getConnections(), createProtocolFilter(jdbc), createGroupFilter(group));
101
102 if (connections.isEmpty()) {
103 log.warn("TMACL-01580:No MSSQL connections were defined. MSSQL support is disabled.");
104 } else {
105 for (Connection connection : connections) {
106 if (connection.getPropertyValue(CONFIGURATION_VERSION, 0D) >= CONFIGURATION_VERSION_VALUE &&
107 connection.getPropertyValue(CONFIGURATION_USE_XA, true) != MSSQL_NO_XA) {
108 if (log.isTraceEnabled()) {
109 log.trace("Not adjusting configuration for {} as XA state is not changed and revision number is not " +
110 "smaller than expected.", connection);
111 }
112 continue;
113 }
114
115 connection.setName(NAME_CONNECTION);
116 connection.setIfRawValue("${not empty " + PROPERTY_HOST + "}");
117
118
119 connection.setAddress("${" + PROPERTY_HOST + "}");
120 if (connection.getPortNumber() == -1) connection.getPort().setRawValue("1433");
121 connection.setProperty(DATABASE_NAME, DEFAULT_DB_NAME);
122
123
124 connection.setProperty(CONFIGURATION_USE_XA, "").setValue(!MSSQL_NO_XA);
125 if (MSSQL_NO_XA) {
126
127 connection.removeProperty("serverName");
128 connection.removeProperty("failoverPartner");
129 connection.removeProperty("portNumber");
130
131
132 connection.setProperty(DATASOURCE_CLASS, "bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
133 if (!MSSQL_USE_AO)
134 connection.setProperty("url", "jdbc:sqlserver://${" + self("address") + "}:${" + self("port") + "};" +
135 "databaseName=" + DEFAULT_DB_NAME + ";failoverPartner=${" + PROPERTY_FAILOVER_HOST + '}');
136 else
137 connection.setProperty("url", "jdbc:sqlserver://${" + self("address") + "}:${" + self("port") + "};" +
138 "databaseName=" + DEFAULT_DB_NAME + ";multiSubnetFailover=true;applicationIntent=${" + PROPERTY_AO_READONLY + '}');
139 connection.setProperty("driverClassName", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
140 } else {
141
142 connection.removeProperty("url");
143 connection.removeProperty("driverClassName");
144
145
146 connection.setProperty(DATASOURCE_CLASS, "com.microsoft.sqlserver.jdbc.SQLServerXADataSource");
147
148 connection.setProperty("serverName", "${" + self("address") + '}');
149 if (!MSSQL_USE_AO)
150 connection.setProperty("failoverPartner", "${" + PROPERTY_FAILOVER_HOST + '}');
151 else {
152 connection.setProperty("multiSubnetFailover", "true");
153 connection.setProperty("applicationIntent", PROPERTY_AO_READONLY);
154 }
155
156 connection.setProperty("portNumber", "${" + self("port") + '}');
157 }
158
159
160 connection.setProperty(MAX_IDLE_TIME, "10");
161 connection.setProperty("ignoreRecoveryFailures", "true");
162 connection.setProperty("enableJdbc4ConnectionTest", "true");
163
164
165 if ("sa".equals(connection.getPropertyValue(USER, "sa"))) {
166 connection.setProperty(USER, DEFAULT_USER_NAME);
167 connection.setProperty(PASSWORD, DEFAULT_PASSWORD);
168 }
169
170
171 connection.setProperty(JpaDatabaseDialect.key(), MSSQL2008Dialect.class.getName());
172 connection.setProperty(JpaSchemaCreationPolicy.key(), "validate");
173
174
175 connection.setProperty(CONFIGURATION_VERSION, String.valueOf(CONFIGURATION_VERSION_VALUE));
176 }
177 }
178 }
179
180 private static String self(String property) {
181 return "connections['" + NAME_CONNECTION + "']['" + property + "']";
182 }
183
184 private void configureJpaSection(Configuration configuration) {
185 PropertySection section = configuration.getPropertySection(JpaSectionTitle.key());
186 if (section != null) {
187 String propertyName = JpaDataSourcePrefix.key() + "CoreDB";
188 String value = "java:${" + self("enabled") + " ? '" + NAME_CONNECTION + "' : 'hsql'}";
189 section.setProperty(propertyName, value);
190 } else {
191 log.warn("TMACL-00960:JPA cannot be configured with MSSQL support, " +
192 "the relevant configuration section is missing (JPA module not in classpath?).");
193 }
194 }
195 }
196
197