]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.akb/src/main/java/org/argeo/slc/akb/core/AkbServiceImpl.java
JDBC, SSH command (+ pseudo file retrieval) and keyring
[gpl/argeo-slc.git] / runtime / org.argeo.slc.akb / src / main / java / org / argeo / slc / akb / core / AkbServiceImpl.java
1 package org.argeo.slc.akb.core;
2
3 import java.net.URI;
4 import java.sql.Connection;
5 import java.sql.DriverManager;
6 import java.sql.PreparedStatement;
7 import java.sql.ResultSet;
8 import java.sql.SQLFeatureNotSupportedException;
9 import java.util.Map;
10
11 import javax.annotation.Resource;
12 import javax.jcr.Node;
13 import javax.jcr.Property;
14 import javax.jcr.Repository;
15 import javax.jcr.RepositoryException;
16 import javax.jcr.Session;
17
18 import org.apache.commons.io.IOUtils;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.argeo.jcr.ArgeoNames;
22 import org.argeo.jcr.JcrUtils;
23 import org.argeo.jcr.UserJcrUtils;
24 import org.argeo.slc.SlcException;
25 import org.argeo.slc.akb.AkbException;
26 import org.argeo.slc.akb.AkbNames;
27 import org.argeo.slc.akb.AkbService;
28 import org.argeo.slc.akb.AkbTypes;
29 import org.argeo.slc.jsch.SimpleUserInfo;
30 import org.argeo.util.security.Keyring;
31
32 import com.jcraft.jsch.ChannelExec;
33 import com.jcraft.jsch.JSch;
34
35 /**
36 * Concrete access to akb services. It provides among other an initialized
37 * environment
38 */
39 public class AkbServiceImpl implements AkbService, AkbNames {
40 private final static Log log = LogFactory.getLog(AkbServiceImpl.class);
41
42 /* DEPENDENCY INJECTION */
43 private Repository repository;
44
45 private Keyring keyring;
46
47 // Populate the repository in a demo context.
48 private Map<String, Resource> demoData = null;
49
50 /* Life cycle management */
51 /**
52 * Call by each startup in order to make sure the backend is ready to
53 * receive/provide data.
54 */
55 public void init() {
56 // JDBC drivers
57 // TODO make it configurable
58 initJdbcDriver("org.postgresql.Driver");
59
60 Session adminSession = null;
61 try {
62 adminSession = repository.login();
63
64 // Initialization of the model
65 if (!adminSession.nodeExists(AKB_TEMPLATES_BASE_PATH)) {
66 JcrUtils.mkdirs(adminSession, AKB_TEMPLATES_BASE_PATH);
67 JcrUtils.mkdirs(adminSession, AKB_ENVIRONMENTS_BASE_PATH);
68 adminSession.save();
69 log.info("Repository has been initialized "
70 + "with AKB's model");
71 }
72
73 // Fill the repository in a demo context
74 if (demoData != null) {
75 // Dev only force reload at each start
76 // if (true) {
77 // if (!projectsPar.hasNodes()) {
78 }
79 } catch (Exception e) {
80 throw new AkbException("Cannot initialize backend", e);
81 } finally {
82 JcrUtils.logoutQuietly(adminSession);
83 }
84 }
85
86 protected Boolean initJdbcDriver(String driver) {
87 try {
88 Class.forName(driver);
89 return true;
90 } catch (ClassNotFoundException e) {
91 if (log.isDebugEnabled())
92 log.debug("Cannot load JDBC driver : " + driver + ", "
93 + e.getMessage());
94 return false;
95 }
96 }
97
98 /** Clean shutdown of the backend. */
99 public void destroy() {
100 }
101
102 @Override
103 public Node createAkbTemplate(Node parentNode, String name)
104 throws RepositoryException {
105 String connectorParentName = "Connectors";
106 String itemsParentName = "Items";
107
108 Node newTemplate = parentNode.addNode(name, AkbTypes.AKB_ENV_TEMPLATE);
109 newTemplate.setProperty(Property.JCR_TITLE, name);
110
111 Node connectorParent = newTemplate.addNode(
112 AkbTypes.AKB_CONNECTOR_FOLDER, AkbTypes.AKB_CONNECTOR_FOLDER);
113 connectorParent.setProperty(Property.JCR_TITLE, connectorParentName);
114
115 Node itemsParent = newTemplate.addNode(AkbTypes.AKB_ITEM_FOLDER,
116 AkbTypes.AKB_ITEM_FOLDER);
117 itemsParent.setProperty(Property.JCR_TITLE, itemsParentName);
118
119 return newTemplate;
120 }
121
122 // ///////////////////////////////////////
123 // / CONNECTORS
124
125 public boolean testConnector(Node connectorNode) {
126 try {
127 if (connectorNode.isNodeType(AkbTypes.AKB_JDBC_CONNECTOR)) {
128 String connectorUrl = connectorNode.getProperty(
129 AKB_CONNECTOR_URL).getString();
130 String connectorUser = connectorNode.getProperty(
131 AKB_CONNECTOR_USER).getString();
132
133 String pwdPath = getPasswordPath(connectorNode);
134 char[] pwd = keyring.getAsChars(pwdPath);
135 DriverManager.getConnection(connectorUrl, connectorUser,
136 new String(pwd));
137 savePassword(connectorNode.getSession(), pwdPath, pwd);
138 return true;
139 } else if (connectorNode.isNodeType(AkbTypes.AKB_SSH_CONNECTOR)) {
140 String connectorUrl = connectorNode.getProperty(
141 AKB_CONNECTOR_URL).getString();
142 String connectorUser = connectorNode.getProperty(
143 AKB_CONNECTOR_USER).getString();
144 String pwdPath = getPasswordPath(connectorNode);
145 char[] pwd = keyring.getAsChars(pwdPath);
146
147 URI url = new URI(connectorUrl);
148 String host = url.getHost();
149 int port = url.getPort();
150 if (port == -1)
151 port = 22;
152 JSch jsch = new JSch();
153 com.jcraft.jsch.Session sess = jsch.getSession(connectorUser,
154 host, port);
155 SimpleUserInfo userInfo = new SimpleUserInfo();
156 userInfo.setPassword(new String(pwd));
157 sess.setUserInfo(userInfo);
158 sess.connect();
159 sess.disconnect();
160
161 savePassword(connectorNode.getSession(), pwdPath, pwd);
162 return true;
163 } else {
164 throw new SlcException("Unsupported connector " + connectorNode);
165 }
166 } catch (Exception e) {
167 throw new SlcException("Cannot test connection", e);
168 }
169 }
170
171 /**
172 * Opens a new connection each time. All resources must be cleaned by
173 * caller.
174 */
175 public PreparedStatement prepareJdbcQuery(Node node) {
176 PreparedStatement statement = null;
177 try {
178 if (node.isNodeType(AkbTypes.AKB_JDBC_QUERY)) {
179 String sqlQuery = node.getProperty(AKB_QUERY_TEXT).getString();
180
181 String connectorPath = node.getProperty(AKB_USED_CONNECTOR)
182 .getString();
183 Node connectorNode = node.getSession().getNode(connectorPath);
184 String connectorUrl = connectorNode.getProperty(
185 AKB_CONNECTOR_URL).getString();
186 String connectorUser = connectorNode.getProperty(
187 AKB_CONNECTOR_USER).getString();
188
189 String pwdPath = getPasswordPath(connectorNode);
190 // String pwdPath = connectorNode.getPath() + '/'
191 // + ArgeoNames.ARGEO_PASSWORD;
192 char[] pwd = keyring.getAsChars(pwdPath);
193 Connection connection = DriverManager.getConnection(
194 connectorUrl, connectorUser, new String(pwd));
195 try {
196 statement = connection.prepareStatement(sqlQuery,
197 ResultSet.TYPE_SCROLL_INSENSITIVE,
198 ResultSet.CONCUR_READ_ONLY);
199 } catch (SQLFeatureNotSupportedException e) {
200 log.warn("Scroll not supported for " + connectorUrl);
201 statement = connection.prepareStatement(sqlQuery);
202 }
203 } else {
204 throw new SlcException("Unsupported node " + node);
205 }
206 return statement;
207 } catch (Exception e) {
208 throw new SlcException("Cannot execute test JDBC query on " + node,
209 e);
210 }
211 }
212
213 public String executeCommand(Node node) {
214 try {
215 String command = node.getProperty(AkbNames.AKB_COMMAND_TEXT)
216 .getString();
217
218 String connectorPath = node.getProperty(AKB_USED_CONNECTOR)
219 .getString();
220 Node connectorNode = node.getSession().getNode(connectorPath);
221 String connectorUrl = connectorNode.getProperty(AKB_CONNECTOR_URL)
222 .getString();
223 String connectorUser = connectorNode
224 .getProperty(AKB_CONNECTOR_USER).getString();
225 String pwdPath = getPasswordPath(connectorNode);
226 char[] pwd = keyring.getAsChars(pwdPath);
227
228 URI url = new URI(connectorUrl);
229 String host = url.getHost();
230 int port = url.getPort();
231 if (port == -1)
232 port = 22;
233 JSch jsch = new JSch();
234 com.jcraft.jsch.Session sess = jsch.getSession(connectorUser, host,
235 port);
236 SimpleUserInfo userInfo = new SimpleUserInfo();
237 userInfo.setPassword(new String(pwd));
238 sess.setUserInfo(userInfo);
239 sess.connect();
240
241 sess.openChannel("exec");
242 final ChannelExec channel = (ChannelExec) sess.openChannel("exec");
243 channel.setCommand(command);
244
245 channel.setInputStream(null);
246 channel.setXForwarding(false);
247 channel.setAgentForwarding(false);
248 channel.setErrStream(null);
249
250 channel.connect();
251
252 String output = IOUtils.toString(channel.getInputStream());
253 channel.disconnect();
254
255 sess.disconnect();
256
257 return output;
258 } catch (Exception e) {
259 throw new SlcException("Cannot execute command", e);
260 }
261
262 }
263
264 public String retrieveFile(Node node) {
265 try {
266 String filePath = node.getProperty(AkbNames.AKB_FILE_PATH)
267 .getString();
268 String command = "cat " + filePath;
269
270 // TODO do a proper scp
271 String connectorPath = node.getProperty(AKB_USED_CONNECTOR)
272 .getString();
273 Node connectorNode = node.getSession().getNode(connectorPath);
274 String connectorUrl = connectorNode.getProperty(AKB_CONNECTOR_URL)
275 .getString();
276 String connectorUser = connectorNode
277 .getProperty(AKB_CONNECTOR_USER).getString();
278 String pwdPath = getPasswordPath(connectorNode);
279 char[] pwd = keyring.getAsChars(pwdPath);
280
281 URI url = new URI(connectorUrl);
282 String host = url.getHost();
283 int port = url.getPort();
284 if (port == -1)
285 port = 22;
286 JSch jsch = new JSch();
287 com.jcraft.jsch.Session sess = jsch.getSession(connectorUser, host,
288 port);
289 SimpleUserInfo userInfo = new SimpleUserInfo();
290 userInfo.setPassword(new String(pwd));
291 sess.setUserInfo(userInfo);
292 sess.connect();
293
294 sess.openChannel("exec");
295 final ChannelExec channel = (ChannelExec) sess.openChannel("exec");
296 channel.setCommand(command);
297
298 channel.setInputStream(null);
299 channel.setXForwarding(false);
300 channel.setAgentForwarding(false);
301 channel.setErrStream(null);
302
303 channel.connect();
304
305 String output = IOUtils.toString(channel.getInputStream());
306 channel.disconnect();
307
308 sess.disconnect();
309
310 return output;
311 } catch (Exception e) {
312 throw new SlcException("Cannot execute command", e);
313 }
314
315 }
316
317 protected String getPasswordPath(Node node) throws RepositoryException {
318 Node home = UserJcrUtils.getUserHome(node.getSession());
319 if (node.getPath().startsWith(home.getPath()))
320 return node.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD;
321 else
322 return home.getPath() + node.getPath() + '/'
323 + ArgeoNames.ARGEO_PASSWORD;
324 }
325
326 private void savePassword(Session session, String pwdPath, char[] pwd)
327 throws RepositoryException {
328 if (!session.itemExists(pwdPath)) {
329 JcrUtils.mkdirs(session, JcrUtils.parentPath(pwdPath));
330 session.save();
331 keyring.set(pwdPath, pwd);
332 }
333
334 }
335
336 // /** Expose injected repository */
337 // public Repository getRepository() {
338 // return repository;
339 // }
340
341 /* DEPENDENCY INJECTION */
342 public void setRepository(Repository repository) {
343 this.repository = repository;
344 }
345
346 public void setDemoData(Map<String, Resource> demoData) {
347 this.demoData = demoData;
348 }
349
350 public void setKeyring(Keyring keyring) {
351 this.keyring = keyring;
352 }
353
354 }