1 package org
.argeo
.slc
.akb
.core
;
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
;
11 import javax
.annotation
.Resource
;
12 import javax
.jcr
.Node
;
13 import javax
.jcr
.NodeIterator
;
14 import javax
.jcr
.Property
;
15 import javax
.jcr
.Repository
;
16 import javax
.jcr
.RepositoryException
;
17 import javax
.jcr
.Session
;
18 import javax
.jcr
.query
.QueryManager
;
19 import javax
.jcr
.query
.QueryResult
;
20 import javax
.jcr
.query
.qom
.Constraint
;
21 import javax
.jcr
.query
.qom
.Ordering
;
22 import javax
.jcr
.query
.qom
.QueryObjectModel
;
23 import javax
.jcr
.query
.qom
.QueryObjectModelConstants
;
24 import javax
.jcr
.query
.qom
.QueryObjectModelFactory
;
25 import javax
.jcr
.query
.qom
.Selector
;
27 import org
.apache
.commons
.io
.IOUtils
;
28 import org
.apache
.commons
.logging
.Log
;
29 import org
.apache
.commons
.logging
.LogFactory
;
30 import org
.argeo
.jcr
.ArgeoNames
;
31 import org
.argeo
.jcr
.JcrUtils
;
32 import org
.argeo
.jcr
.UserJcrUtils
;
33 import org
.argeo
.slc
.SlcException
;
34 import org
.argeo
.slc
.akb
.AkbException
;
35 import org
.argeo
.slc
.akb
.AkbNames
;
36 import org
.argeo
.slc
.akb
.AkbService
;
37 import org
.argeo
.slc
.akb
.AkbTypes
;
38 import org
.argeo
.slc
.akb
.utils
.AkbJcrUtils
;
39 import org
.argeo
.slc
.jsch
.SimpleUserInfo
;
40 import org
.argeo
.util
.security
.Keyring
;
42 import com
.jcraft
.jsch
.ChannelExec
;
43 import com
.jcraft
.jsch
.JSch
;
46 * Concrete access to akb services. It provides among other an initialized
49 public class AkbServiceImpl
implements AkbService
, AkbNames
{
50 private final static Log log
= LogFactory
.getLog(AkbServiceImpl
.class);
52 /* DEPENDENCY INJECTION */
53 private Repository repository
;
55 private Keyring keyring
;
57 // Populate the repository in a demo context.
58 private Map
<String
, Resource
> demoData
= null;
60 /* Life cycle management */
62 * Call by each startup in order to make sure the backend is ready to
63 * receive/provide data.
67 // TODO make it configurable
68 initJdbcDriver("org.postgresql.Driver");
70 Session adminSession
= null;
72 adminSession
= repository
.login();
74 // Initialization of the model
75 if (!adminSession
.nodeExists(AKB_TEMPLATES_BASE_PATH
)) {
76 JcrUtils
.mkdirs(adminSession
, AKB_TEMPLATES_BASE_PATH
);
77 JcrUtils
.mkdirs(adminSession
, AKB_ENVIRONMENTS_BASE_PATH
);
79 log
.info("Repository has been initialized "
80 + "with AKB's model");
83 // Fill the repository in a demo context
84 if (demoData
!= null) {
85 // Dev only force reload at each start
87 // if (!projectsPar.hasNodes()) {
89 } catch (Exception e
) {
90 throw new AkbException("Cannot initialize backend", e
);
92 JcrUtils
.logoutQuietly(adminSession
);
96 protected Boolean
initJdbcDriver(String driver
) {
98 Class
.forName(driver
);
100 } catch (ClassNotFoundException e
) {
101 if (log
.isDebugEnabled())
102 log
.debug("Cannot load JDBC driver : " + driver
+ ", "
108 /** Clean shutdown of the backend. */
109 public void destroy() {
113 public Node
createAkbTemplate(Node parentNode
, String name
)
114 throws RepositoryException
{
115 Node newTemplate
= parentNode
.addNode(name
, AkbTypes
.AKB_ENV_TEMPLATE
);
116 newTemplate
.setProperty(Property
.JCR_TITLE
, name
);
117 newTemplate
.addNode(AkbTypes
.AKB_CONNECTOR_FOLDER
,
118 AkbTypes
.AKB_CONNECTOR_FOLDER
);
122 // //////////////////////////
125 public Node
createActiveEnv(Node template
, String name
,
126 boolean copyDefaultConnectors
) throws RepositoryException
{
128 Session session
= template
.getSession();
129 Node parentEnvNode
= session
.getNode(AKB_ENVIRONMENTS_BASE_PATH
);
130 Node createdEnv
= parentEnvNode
.addNode(name
, AkbTypes
.AKB_ENV
);
131 createdEnv
.setProperty(AKB_ENV_TEMPLATE_PATH
, template
.getPath());
132 createdEnv
.setProperty(Property
.JCR_TITLE
, name
);
134 Node connectorParent
= createdEnv
.addNode(
135 AkbTypes
.AKB_CONNECTOR_FOLDER
, AkbTypes
.AKB_CONNECTOR_FOLDER
);
137 NodeIterator ni
= template
.getNode(AkbTypes
.AKB_CONNECTOR_FOLDER
)
139 activeConns
: while (ni
.hasNext()) {
140 Node currNode
= ni
.nextNode();
141 if (currNode
.isNodeType(AkbTypes
.AKB_CONNECTOR_ALIAS
)) {
142 String connType
= currNode
.getProperty(AKB_CONNECTOR_TYPE
)
145 if (AkbJcrUtils
.isEmptyString(connType
))
146 // Cannot create an instance if the type is undefined
147 continue activeConns
;
149 Node newConnector
= connectorParent
.addNode(currNode
.getName(),
151 newConnector
.setProperty(AKB_CONNECTOR_ALIAS_PATH
,
153 if (copyDefaultConnectors
155 .hasNode(AkbNames
.AKB_DEFAULT_TEST_CONNECTOR
)) {
156 Node defaultConn
= currNode
157 .getNode(AkbNames
.AKB_DEFAULT_TEST_CONNECTOR
);
158 if (defaultConn
.hasProperty(AkbNames
.AKB_CONNECTOR_URL
))
161 AkbNames
.AKB_CONNECTOR_URL
,
162 defaultConn
.getProperty(
163 AkbNames
.AKB_CONNECTOR_URL
)
165 if (defaultConn
.hasProperty(AkbNames
.AKB_CONNECTOR_USER
))
166 newConnector
.setProperty(
167 AkbNames
.AKB_CONNECTOR_USER
,
168 defaultConn
.getProperty(
169 AkbNames
.AKB_CONNECTOR_USER
)
177 // ///////////////////////////////////////
181 public Node
createConnectorAlias(Node templateNode
, String name
,
182 String connectorType
) throws RepositoryException
{
183 Node parent
= JcrUtils
.mkdirs(templateNode
,
184 AkbTypes
.AKB_CONNECTOR_FOLDER
, AkbTypes
.AKB_CONNECTOR_FOLDER
);
185 Node newConnector
= parent
.addNode(name
, AkbTypes
.AKB_CONNECTOR_ALIAS
);
186 newConnector
.setProperty(Property
.JCR_TITLE
, name
);
187 newConnector
.setProperty(AkbNames
.AKB_CONNECTOR_TYPE
, connectorType
);
189 // Node defaultConnector =
190 Node defaultConn
= newConnector
.addNode(
191 AkbNames
.AKB_DEFAULT_TEST_CONNECTOR
, connectorType
);
192 defaultConn
.setProperty(AkbNames
.AKB_CONNECTOR_ALIAS_PATH
,
193 newConnector
.getPath());
198 public NodeIterator
getDefinedAliases(Node itemTemplate
,
199 String connectorType
) throws RepositoryException
{
201 Session session
= itemTemplate
.getSession();
202 QueryManager queryManager
= session
.getWorkspace()
204 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
206 Selector source
= factory
.selector(AkbTypes
.AKB_CONNECTOR_ALIAS
,
207 AkbTypes
.AKB_CONNECTOR_ALIAS
);
208 Constraint defaultC
= factory
.descendantNode(
209 source
.getSelectorName(), itemTemplate
.getPath());
211 if (connectorType
!= null) {
212 Constraint connType
= factory
.comparison(factory
.propertyValue(
213 source
.getSelectorName(), AkbNames
.AKB_CONNECTOR_TYPE
),
214 QueryObjectModelConstants
.JCR_OPERATOR_EQUAL_TO
,
215 factory
.literal(session
.getValueFactory().createValue(
217 defaultC
= factory
.and(defaultC
, connType
);
220 // Order by default by JCR TITLE
221 // TODO check if node definition has MIX_TITLE mixin
222 // TODO Apparently case insensitive ordering is not implemented in
223 // current used JCR implementation
224 Ordering order
= factory
225 .ascending(factory
.upperCase(factory
.propertyValue(
226 source
.getSelectorName(), Property
.JCR_TITLE
)));
227 QueryObjectModel query
;
228 query
= factory
.createQuery(source
, defaultC
,
229 new Ordering
[] { order
}, null);
230 QueryResult result
= query
.execute();
231 return result
.getNodes();
232 } catch (RepositoryException e
) {
233 throw new AkbException("Unable to list connector", e
);
238 public Node
getActiveConnectorByAlias(Node envNode
, String aliasPath
)
239 throws RepositoryException
{
241 Session session
= envNode
.getSession();
242 QueryManager queryManager
= session
.getWorkspace()
244 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
246 Selector source
= factory
.selector(AkbTypes
.AKB_CONNECTOR
,
247 AkbTypes
.AKB_CONNECTOR
);
248 Constraint defaultC
= factory
.descendantNode(
249 source
.getSelectorName(), envNode
.getPath());
251 Constraint connType
= factory
.comparison(
252 factory
.propertyValue(source
.getSelectorName(),
253 AkbNames
.AKB_CONNECTOR_ALIAS_PATH
),
254 QueryObjectModelConstants
.JCR_OPERATOR_EQUAL_TO
, factory
255 .literal(session
.getValueFactory().createValue(
257 defaultC
= factory
.and(defaultC
, connType
);
259 QueryObjectModel query
;
260 query
= factory
.createQuery(source
, defaultC
, null, null);
261 QueryResult result
= query
.execute();
262 NodeIterator ni
= result
.getNodes();
267 Node connector
= ni
.nextNode();
269 throw new AkbException("More than one alias with name "
270 + aliasPath
+ " has been defined for environment "
275 } catch (RepositoryException e
) {
276 throw new AkbException("Unable to get connector " + aliasPath
277 + " in " + envNode
, e
);
282 public boolean testConnector(Node connectorNode
) {
284 if (connectorNode
.isNodeType(AkbTypes
.AKB_JDBC_CONNECTOR
)) {
285 String connectorUrl
= connectorNode
.getProperty(
286 AKB_CONNECTOR_URL
).getString();
287 String connectorUser
= connectorNode
.getProperty(
288 AKB_CONNECTOR_USER
).getString();
290 String pwdPath
= getPasswordPath(connectorNode
);
291 char[] pwd
= keyring
.getAsChars(pwdPath
);
292 DriverManager
.getConnection(connectorUrl
, connectorUser
,
294 savePassword(connectorNode
.getSession(), pwdPath
, pwd
);
296 } else if (connectorNode
.isNodeType(AkbTypes
.AKB_SSH_CONNECTOR
)) {
297 String connectorUrl
= connectorNode
.getProperty(
298 AKB_CONNECTOR_URL
).getString();
299 String connectorUser
= connectorNode
.getProperty(
300 AKB_CONNECTOR_USER
).getString();
301 String pwdPath
= getPasswordPath(connectorNode
);
302 char[] pwd
= keyring
.getAsChars(pwdPath
);
304 URI url
= new URI(connectorUrl
);
305 String host
= url
.getHost();
306 int port
= url
.getPort();
309 JSch jsch
= new JSch();
310 com
.jcraft
.jsch
.Session sess
= jsch
.getSession(connectorUser
,
312 SimpleUserInfo userInfo
= new SimpleUserInfo();
313 userInfo
.setPassword(new String(pwd
));
314 sess
.setUserInfo(userInfo
);
318 savePassword(connectorNode
.getSession(), pwdPath
, pwd
);
321 throw new SlcException("Unsupported connector " + connectorNode
);
323 } catch (Exception e
) {
324 throw new SlcException("Cannot test connection", e
);
329 * Opens a new connection each time. All resources must be cleaned by
332 public PreparedStatement
prepareJdbcQuery(Node activeEnv
, Node node
) {
333 PreparedStatement statement
= null;
336 if (node
.isNodeType(AkbTypes
.AKB_JDBC_QUERY
)) {
337 String connectorAliasStr
= node
.getProperty(AKB_USED_CONNECTOR
)
339 // in case of a template passed env can be null
340 if (activeEnv
== null) {
341 activeEnv
= AkbJcrUtils
.getCurrentTemplate(node
);
343 Node connectorNode
= getActiveConnectorByAlias(activeEnv
,
346 String sqlQuery
= node
.getProperty(AKB_QUERY_TEXT
).getString();
348 String connectorUrl
= AkbJcrUtils
.get(connectorNode
,
350 String connectorUser
= AkbJcrUtils
.get(connectorNode
,
354 if (AkbJcrUtils
.isEmptyString(connectorUrl
)
355 || AkbJcrUtils
.isEmptyString(connectorUser
))
358 String pwdPath
= getPasswordPath(connectorNode
);
359 // String pwdPath = connectorNode.getPath() + '/'
360 // + ArgeoNames.ARGEO_PASSWORD;
361 char[] pwd
= keyring
.getAsChars(pwdPath
);
362 Connection connection
= DriverManager
.getConnection(
363 connectorUrl
, connectorUser
, new String(pwd
));
365 statement
= connection
.prepareStatement(sqlQuery
,
366 ResultSet
.TYPE_SCROLL_INSENSITIVE
,
367 ResultSet
.CONCUR_READ_ONLY
);
368 } catch (SQLFeatureNotSupportedException e
) {
369 log
.warn("Scroll not supported for " + connectorUrl
);
370 statement
= connection
.prepareStatement(sqlQuery
);
373 throw new SlcException("Unsupported node " + node
);
376 } catch (Exception e
) {
377 throw new SlcException("Cannot execute test JDBC query on " + node
,
382 public String
executeCommand(Node activeEnv
, Node node
) {
385 String connectorAliasStr
= node
.getProperty(AKB_USED_CONNECTOR
)
387 // in case of a template passed env can be null
388 if (activeEnv
== null) {
389 activeEnv
= AkbJcrUtils
.getCurrentTemplate(node
);
391 Node connectorNode
= getActiveConnectorByAlias(activeEnv
,
393 String command
= node
.getProperty(AkbNames
.AKB_COMMAND_TEXT
)
396 String connectorUrl
= AkbJcrUtils
.get(connectorNode
,
398 String connectorUser
= AkbJcrUtils
.get(connectorNode
,
402 if (AkbJcrUtils
.isEmptyString(connectorUrl
)
403 || AkbJcrUtils
.isEmptyString(connectorUser
))
406 String pwdPath
= getPasswordPath(connectorNode
);
407 char[] pwd
= keyring
.getAsChars(pwdPath
);
409 URI url
= new URI(connectorUrl
);
410 String host
= url
.getHost();
411 int port
= url
.getPort();
414 JSch jsch
= new JSch();
415 com
.jcraft
.jsch
.Session sess
= jsch
.getSession(connectorUser
, host
,
417 SimpleUserInfo userInfo
= new SimpleUserInfo();
418 userInfo
.setPassword(new String(pwd
));
419 sess
.setUserInfo(userInfo
);
422 sess
.openChannel("exec");
423 final ChannelExec channel
= (ChannelExec
) sess
.openChannel("exec");
424 channel
.setCommand(command
);
426 channel
.setInputStream(null);
427 channel
.setXForwarding(false);
428 channel
.setAgentForwarding(false);
429 channel
.setErrStream(null);
433 String output
= IOUtils
.toString(channel
.getInputStream());
434 channel
.disconnect();
439 } catch (Exception e
) {
440 throw new SlcException("Cannot execute command", e
);
445 public String
retrieveFile(Node activeEnv
, Node node
) {
447 String filePath
= node
.getProperty(AkbNames
.AKB_FILE_PATH
)
449 String command
= "cat " + filePath
;
451 String connectorAliasStr
= node
.getProperty(AKB_USED_CONNECTOR
)
453 // in case of a template passed env can be null
454 if (activeEnv
== null) {
455 activeEnv
= AkbJcrUtils
.getCurrentTemplate(node
);
457 Node connectorNode
= getActiveConnectorByAlias(activeEnv
,
460 // TODO do a proper scp
461 String connectorUrl
= AkbJcrUtils
.get(connectorNode
,
463 String connectorUser
= AkbJcrUtils
.get(connectorNode
,
467 if (AkbJcrUtils
.isEmptyString(connectorUrl
)
468 || AkbJcrUtils
.isEmptyString(connectorUser
))
471 String pwdPath
= getPasswordPath(connectorNode
);
472 char[] pwd
= keyring
.getAsChars(pwdPath
);
474 URI url
= new URI(connectorUrl
);
475 String host
= url
.getHost();
476 int port
= url
.getPort();
479 JSch jsch
= new JSch();
480 com
.jcraft
.jsch
.Session sess
= jsch
.getSession(connectorUser
, host
,
482 SimpleUserInfo userInfo
= new SimpleUserInfo();
483 userInfo
.setPassword(new String(pwd
));
484 sess
.setUserInfo(userInfo
);
487 sess
.openChannel("exec");
488 final ChannelExec channel
= (ChannelExec
) sess
.openChannel("exec");
489 channel
.setCommand(command
);
491 channel
.setInputStream(null);
492 channel
.setXForwarding(false);
493 channel
.setAgentForwarding(false);
494 channel
.setErrStream(null);
498 String output
= IOUtils
.toString(channel
.getInputStream());
499 channel
.disconnect();
504 } catch (Exception e
) {
505 throw new SlcException("Cannot execute command", e
);
510 protected String
getPasswordPath(Node node
) throws RepositoryException
{
511 Node home
= UserJcrUtils
.getUserHome(node
.getSession());
512 if (node
.getPath().startsWith(home
.getPath()))
513 return node
.getPath() + '/' + ArgeoNames
.ARGEO_PASSWORD
;
515 return home
.getPath() + node
.getPath() + '/'
516 + ArgeoNames
.ARGEO_PASSWORD
;
519 private void savePassword(Session session
, String pwdPath
, char[] pwd
)
520 throws RepositoryException
{
521 if (!session
.itemExists(pwdPath
)) {
522 JcrUtils
.mkdirs(session
, JcrUtils
.parentPath(pwdPath
));
524 keyring
.set(pwdPath
, pwd
);
529 /** Expose injected repository */
530 public Repository
getRepository() {
534 /* DEPENDENCY INJECTION */
535 public void setRepository(Repository repository
) {
536 this.repository
= repository
;
539 public void setDemoData(Map
<String
, Resource
> demoData
) {
540 this.demoData
= demoData
;
543 public void setKeyring(Keyring keyring
) {
544 this.keyring
= keyring
;