View Javadoc
1   package org.argeo.slc.client.ui.dist.model;
2   
3   import java.security.AccessControlException;
4   
5   import javax.jcr.Credentials;
6   import javax.jcr.Node;
7   import javax.jcr.Repository;
8   import javax.jcr.RepositoryException;
9   import javax.jcr.RepositoryFactory;
10  import javax.jcr.Session;
11  
12  import org.argeo.cms.ArgeoNames;
13  import org.argeo.jcr.JcrUtils;
14  import org.argeo.node.NodeUtils;
15  import org.argeo.node.security.Keyring;
16  import org.argeo.slc.SlcException;
17  import org.argeo.slc.repo.RepoConstants;
18  import org.argeo.slc.repo.RepoUtils;
19  
20  /**
21   * Abstract a repository. It might be persisted by a node in the current user
22   * home Node or just an URI and a label if user is anonymous
23   */
24  public class RepoElem extends DistParentElem {
25  	// private final static Log log = LogFactory.getLog(RepoElem.class);
26  
27  	private RepositoryFactory repositoryFactory;
28  	private Keyring keyring;
29  	private Credentials credentials;
30  	private Session defaultSession = null;
31  
32  	// Defines current repo
33  	private Node repoNode = null;
34  	private String label;
35  	private String uri;
36  
37  	private Repository repository;
38  
39  	/**
40  	 * Creates a RepoElement for anonymous user. The {@code RepositoryFactory}
41  	 * is used to enable lazy initialisation
42  	 */
43  	public RepoElem(RepositoryFactory repoFactory, String uri, String label) {
44  		super(label);
45  		this.repositoryFactory = repoFactory;
46  		this.uri = uri;
47  		this.label = label;
48  	}
49  
50  	/**
51  	 * Creates a RepoElement for an authenticated user. The
52  	 * {@code RepositoryFactory} and {@code Keyring} are used to enable lazy
53  	 * initialisation
54  	 * 
55  	 */
56  	public RepoElem(RepositoryFactory repoFactory, Keyring keyring, Node repoNode, String alias) {
57  		super(alias);
58  		this.label = alias;
59  		// label = repoNode.isNodeType(NodeType.MIX_TITLE) ? repoNode
60  		// .getProperty(Property.JCR_TITLE).getString() : repoNode
61  		// .getName();
62  		this.repoNode = repoNode;
63  		this.repositoryFactory = repoFactory;
64  		this.keyring = keyring;
65  		try {
66  			// Initialize this repo information
67  			setInHome(RepoConstants.DEFAULT_JAVA_REPOSITORY_ALIAS.equals(repoNode.getName()));
68  			if (inHome())
69  				// Directly log and retrieve children for local repository
70  				login();
71  			else
72  				setReadOnly(!repoNode.hasNode(ArgeoNames.ARGEO_PASSWORD));
73  			uri = JcrUtils.get(repoNode, ArgeoNames.ARGEO_URI);
74  		} catch (RepositoryException e) {
75  			throw new SlcException("Unable to " + "initialize repo element", e);
76  		}
77  	}
78  
79  	/** Effective login. Does nothing if the session is already there. */
80  	public void login() {
81  		if (isConnected())
82  			return;
83  
84  		if (repository == null)
85  			if (repoNode == null)
86  				// Anonymous
87  				repository = NodeUtils.getRepositoryByUri(repositoryFactory, uri);
88  			else {
89  				repository = RepoUtils.getRepository(repositoryFactory, keyring, repoNode);
90  				credentials = RepoUtils.getRepositoryCredentials(keyring, repoNode);
91  			}
92  
93  		try {
94  			// FIXME make it more generic
95  			String defaultWorkspace = "main";
96  			defaultSession = repository.login(credentials, defaultWorkspace);
97  			refreshChildren();
98  		} catch (RepositoryException e) {
99  			throw new SlcException("Cannot login repository " + label + " with credential " + credentials, e);
100 		}
101 	}
102 
103 	protected void refreshChildren() {
104 		try {
105 			// TODO also remove deleted children (only adds for the time being
106 			String[] workspaceNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames();
107 			buildWksp: for (String workspaceName : workspaceNames) {
108 				if (!isWorkspaceVisible(workspaceName))
109 					continue buildWksp;
110 
111 				String prefix = getPrefix(workspaceName);
112 				if (getChildByName(prefix) == null) {
113 					WkspGroupElem wkspGpElem = new WkspGroupElem(RepoElem.this, prefix);
114 					addChild(wkspGpElem);
115 				}
116 			}
117 		} catch (RepositoryException e) {
118 			throw new SlcException("Cannot list workspaces for " + repoNode, e);
119 		}
120 	}
121 
122 	@Override
123 	public synchronized void dispose() {
124 		JcrUtils.logoutQuietly(defaultSession);
125 		super.dispose();
126 	}
127 
128 	private String getPrefix(String workspaceName) {
129 		// Here is the tricks - we rely on a "hard coded" convention
130 		// Workspace name should be like: name-major.minor
131 		if (workspaceName.lastIndexOf(VERSION_SEP) > 0)
132 			return workspaceName.substring(0, workspaceName.lastIndexOf(VERSION_SEP));
133 		else
134 			return workspaceName;
135 	}
136 
137 	/* Exposes this to the children workspace group */
138 	protected boolean isWorkspaceVisible(String wkspName) {
139 		Boolean result = true;
140 		if (ARGEO_SYSTEM_WKSP.contains(wkspName))
141 			return false;
142 		// Add a supplementary check to hide workspace that are not
143 		// public to anonymous user
144 		if (repoNode == null) {
145 			Session tmpSession = null;
146 			try {
147 				tmpSession = repository.login(wkspName);
148 				try {
149 					tmpSession.checkPermission("/", "read");
150 				} catch (AccessControlException e) {
151 					result = false;
152 				}
153 			} catch (RepositoryException e) {
154 				throw new SlcException("Cannot list workspaces for anonymous user", e);
155 			} finally {
156 				JcrUtils.logoutQuietly(tmpSession);
157 			}
158 		}
159 		return result;
160 	}
161 
162 	/**
163 	 * Actual call to the
164 	 * {@link Repository#login(javax.jcr.Credentials, String)} method. To be
165 	 * overridden.
166 	 * 
167 	 * Creates a new session with correct credentials using the information
168 	 * contained in the corresponding repo node. It provides all UI children
169 	 * elements an unique entry point to retrieve a new Session. Caller must
170 	 * close the session when it is not in use anymore.
171 	 * 
172 	 */
173 	protected Session repositoryLogin(String workspaceName) {
174 		try {
175 			if (workspaceName == null)
176 				workspaceName = "main";// FIXME make it more generic
177 			return repository.login(credentials, workspaceName);
178 		} catch (RepositoryException e) {
179 			throw new SlcException("Cannot login repository " + label + " with credential " + credentials, e);
180 		}
181 	}
182 
183 	public Boolean isConnected() {
184 		if (defaultSession != null && defaultSession.isLive())
185 			return true;
186 		else
187 			return false;
188 	}
189 
190 	/** Exposes URI to the current repository */
191 	public String getUri() {
192 		return uri;
193 	}
194 
195 	public String getRepoNodePath() {
196 		if (repoNode == null)
197 			return null;
198 		else
199 			try {
200 				return repoNode.getPath();
201 			} catch (RepositoryException e) {
202 				throw new SlcException("Cannot get node path for repository " + label, e);
203 			}
204 	}
205 
206 	/**
207 	 * Exposes the local repoNode that completely define a connection to a
208 	 * repository (including a set of credentials). Might return null in case of
209 	 * an anonymous user
210 	 */
211 	protected Node getRepoNode() {
212 		return repoNode;
213 	}
214 
215 	protected Repository getRepository() {
216 		return repository;
217 	}
218 
219 	protected Credentials getCredentials() {
220 		return credentials;
221 	}
222 
223 	// META INFO
224 	public String getDescription() {
225 		String desc = label;
226 		if (repoNode != null)
227 			desc = label + " (" + uri + ")";
228 		return desc;
229 	}
230 
231 	public String getLabel() {
232 		return label;
233 	}
234 
235 	public String toString() {
236 		return repoNode != null ? repoNode.toString() : label;
237 	}
238 }