001: /**
002: * LibreSource
003: * Copyright (C) 2004-2008 Artenum SARL / INRIA
004: * http://www.libresource.org - contact@artenum.com
005: *
006: * This file is part of the LibreSource software,
007: * which can be used and distributed under license conditions.
008: * The license conditions are provided in the LICENSE.TXT file
009: * at the root path of the packaging that enclose this file.
010: * More information can be found at
011: * - http://dev.libresource.org/home/license
012: *
013: * Initial authors :
014: *
015: * Guillaume Bort / INRIA
016: * Francois Charoy / Universite Nancy 2
017: * Julien Forest / Artenum
018: * Claude Godart / Universite Henry Poincare
019: * Florent Jouille / INRIA
020: * Sebastien Jourdain / INRIA / Artenum
021: * Yves Lerumeur / Artenum
022: * Pascal Molli / Universite Henry Poincare
023: * Gerald Oster / INRIA
024: * Mariarosa Penzi / Artenum
025: * Gerard Sookahet / Artenum
026: * Raphael Tani / INRIA
027: *
028: * Contributors :
029: *
030: * Stephane Bagnier / Artenum
031: * Amadou Dia / Artenum-IUP Blois
032: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
033: */package org.libresource.so6.core;
034:
035: import org.libresource.so6.core.client.ClientI;
036: import org.libresource.so6.core.engine.util.FileUtils;
037: import org.libresource.so6.core.engine.util.UniqueId;
038:
039: import java.io.File;
040: import java.io.FileFilter;
041: import java.io.FileInputStream;
042: import java.io.FileOutputStream;
043: import java.io.FileWriter;
044: import java.io.FilenameFilter;
045: import java.io.IOException;
046:
047: import java.util.ArrayList;
048: import java.util.Properties;
049:
050: /**
051: * The <code>Workspace</code> class represents your local shared directory in
052: * the so6 representation. Any workspace can be connected to one or several So6
053: * Queue. Those connection are represented by the <code>WsConnection<code>
054: * class and all the synchronisation operations will be made on those connections.
055: * <p>
056: * The <code>Workspace</code> class is just there to federate those connections
057: * in order to build and represent the synchronisation network.
058: * <p>
059: * Workspace are identified by an unique ID and have several queue connections.
060: *
061: * @author Smack
062: * @version 1.0, 26/05/04
063: * @see org.libresource.so6.core.WsConnection
064: * @see org.libresource.so6.core.engine.util.UniqueId
065: * @since JDK1.4
066: */
067: public class Workspace {
068: /** Name of the file where the workspace id will be stored */
069: public static final String SO6_WS_FILE = "workspaceId";
070:
071: /**
072: * Name of the data directory where the Workspace store the meta data (Id,
073: * Connections...)
074: */
075: public static final String SO6PREFIX = ".so6";
076:
077: //
078: private String wsBasePath;
079:
080: /**
081: * Load an existing <code>Workspace</code> object in order to access to
082: * its id and WsConnections.
083: *
084: * @param wsBasePath
085: * workspace base path a <code>String</code>.
086: */
087: public Workspace(String wsBasePath) throws IOException {
088: this .wsBasePath = wsBasePath;
089:
090: File f = new File(wsBasePath + File.separator + SO6PREFIX
091: + File.separator + SO6_WS_FILE);
092:
093: if (!f.exists()) {
094: throw new IOException("Invalide workspace path ("
095: + wsBasePath + ")");
096: }
097: }
098:
099: /**
100: * Create a new <code>Workspace</code> object and generate its unique
101: * identifier
102: *
103: * @param basePath
104: * workspace base path a <code>String</code>.
105: */
106: public static Workspace createWorkspace(String basePath)
107: throws IOException {
108: File ws = new File(basePath + File.separator + SO6PREFIX,
109: SO6_WS_FILE);
110:
111: if (ws.exists()) {
112: return new Workspace(basePath);
113: }
114:
115: FileUtils.createDirIfNotExist(ws.getParent());
116:
117: if (ws.createNewFile()) {
118: FileWriter fw = new FileWriter(ws);
119: fw.write(UniqueId.getUniqueId());
120: fw.close();
121:
122: return new Workspace(basePath);
123: } else {
124: throw new IOException(
125: "Unable to create a new workspace: Maybe filesystem failure");
126: }
127: }
128:
129: /**
130: * Returns the unique id as a string using caratere [a-z,A-Z,0-9,_] (The
131: * content of the file workspaceId)
132: *
133: * @return the workspace id as a String
134: */
135: public String getId() throws IOException {
136: return extractIdFromFile(new File(wsBasePath + File.separator
137: + SO6PREFIX, SO6_WS_FILE));
138: }
139:
140: /**
141: * Create a new <code>WsConnection</code> linked to that workspace.
142: * <p>
143: * As WsConnection can be made to any kind of So6 Queue, we let the user set
144: * his personal connections properties, then we set the default ones.
145: *
146: * @param externalProperties
147: * external properties need to init the clientI class
148: * @param clientIClassName
149: * class to call when we use the connection
150: * @param connectionName
151: * name of that connection
152: *
153: * @return the file path of the WsConnection propertie file
154: *
155: * @exception Exception
156: * If any error occured during the creation of the connection
157: * files
158: */
159: public String createConnection(Properties externalProperties,
160: String clientIClassName, String connectionName)
161: throws IOException {
162: int i = 1;
163: File wsBaseDataDir = new File(wsBasePath + File.separator
164: + SO6PREFIX, "" + i);
165:
166: while (wsBaseDataDir.exists()) {
167: wsBaseDataDir = new File(wsBasePath + File.separator
168: + SO6PREFIX, new Integer(i++).toString());
169: }
170:
171: if (!wsBaseDataDir.mkdirs()) {
172: throw new IOException(
173: "Unable to create workspace data dir : "
174: + wsBaseDataDir.getPath());
175: }
176:
177: String dataDir = wsBaseDataDir.getAbsolutePath();
178: String propFilePath = dataDir + File.separator
179: + WsConnection.SO6_WSC_FILE;
180:
181: //
182: externalProperties.setProperty(WsConnection.PATH, new File(
183: wsBasePath).getAbsolutePath());
184: externalProperties.setProperty(WsConnection.WS_NAME,
185: connectionName);
186: externalProperties.setProperty(WsConnection.SYNC_CLIENT_NAME,
187: clientIClassName);
188: externalProperties.setProperty(WsConnection.DATAPATH, dataDir);
189:
190: FileOutputStream fos = new FileOutputStream(propFilePath);
191: externalProperties.store(fos, "Do not edit");
192: fos.close();
193:
194: return propFilePath;
195: }
196:
197: /**
198: * Remove a <code>WsConnection</code>
199: *
200: * @param wsConnectionPath
201: * absolute file path of a WsConnection property file
202: * (so6.properties)
203: *
204: * @exception Exception
205: * If any error occured during the removed of the connection
206: * files
207: */
208: public static void deleteConnection(String wsConnectionPath)
209: throws Exception {
210: File f = new File(wsConnectionPath).getParentFile();
211:
212: if (f.getParentFile().getName().equals(SO6PREFIX)) {
213: FileUtils.remove(f.getPath());
214: } else {
215: throw new Exception(
216: "Invalide workpsace connection path value (ex: /exemple/"
217: + SO6PREFIX + "/1/"
218: + WsConnection.SO6_WSC_FILE + ")");
219: }
220: }
221:
222: /**
223: * Remove a <code>WsConnection</code> linked to that workspace.
224: *
225: * @param connectionNumber
226: * the connection number of that workspace
227: *
228: * @exception Exception
229: * If any error occured during the removed of the connection
230: * files
231: */
232: public void deleteConnection(int connectionNumber) throws Exception {
233: File f = new File(wsBasePath, Integer
234: .toString(connectionNumber));
235:
236: if (!f.exists()) {
237: throw new Exception("Invalide workpsace connection number");
238: }
239:
240: FileUtils.remove(f.getPath());
241: }
242:
243: /**
244: * List the <code>WsConnection</code> linked to that workspace.
245: *
246: * @return an array of the linked WsConnection
247: *
248: * @exception Exception
249: * If any error occured during the removed of the connection
250: * files
251: */
252: public WsConnection[] listConnections() {
253: File dataDir = new File(wsBasePath, SO6PREFIX);
254: File[] connections = dataDir.listFiles(new FileFilter() {
255: public boolean accept(File pathname) {
256: return pathname.isDirectory();
257: }
258: });
259:
260: ArrayList tmpResult = new ArrayList();
261:
262: for (int i = 0; i < connections.length; i++) {
263: try {
264: tmpResult.add(new WsConnection(connections[i]
265: .getAbsolutePath()
266: + File.separator + WsConnection.SO6_WSC_FILE));
267: } catch (Exception e) {
268: }
269: }
270:
271: WsConnection[] wsConnection = new WsConnection[tmpResult.size()];
272:
273: for (int i = 0; i < wsConnection.length; i++) {
274: wsConnection[i] = (WsConnection) tmpResult.get(i);
275: }
276:
277: return wsConnection;
278: }
279:
280: /**
281: * Get a <code>WsConnection</code> linked to that workspace.
282: * <p>
283: * If the parameter is
284: * <code>null<code> then the connection number will be 1.
285: *
286: * @param connectionNumber the connection number of that WsConnection
287: *
288: * @return a WsConnection
289: *
290: * @exception Exception If any error occured during the removed of the connection files
291: */
292: public WsConnection getConnectionByQueueId(String queueId)
293: throws Exception {
294: File basePath = new File(wsBasePath + File.separator
295: + SO6PREFIX);
296: File[] connections = basePath.listFiles();
297:
298: for (int i = 0; i < connections.length; i++) {
299: File propsFile = new File(basePath,
300: WsConnection.SO6_WSC_FILE);
301:
302: if (propsFile.exists()) {
303: Properties props = new Properties();
304: FileInputStream fis = new FileInputStream(propsFile);
305: props.load(fis);
306: fis.close();
307:
308: if (props.getProperty(ClientI.SO6_QUEUE_ID).equals(
309: queueId)) {
310: return new WsConnection(propsFile.getAbsolutePath()
311: + File.separator
312: + WsConnection.SO6_WSC_FILE);
313: }
314: }
315: }
316:
317: throw new Exception("Unable to find the specified replica");
318: }
319:
320: public WsConnection getConnection(String connectionNumber)
321: throws Exception {
322: if (connectionNumber == null) {
323: connectionNumber = "1";
324: }
325:
326: File propFile = new File(wsBasePath + File.separator
327: + SO6PREFIX + File.separator + connectionNumber
328: + File.separator + WsConnection.SO6_WSC_FILE);
329:
330: if (propFile.exists()) {
331: return new WsConnection(propFile.getAbsolutePath());
332: } else {
333: throw new Exception(
334: "Invalide relative workspace connection path");
335: }
336: }
337:
338: // Private methodes
339: private String extractIdFromFile(File f) throws IOException {
340: StringBuffer result = new StringBuffer();
341: FileInputStream fis = new FileInputStream(f);
342: byte[] buffer = new byte[1024];
343: int length;
344:
345: while ((length = fis.read(buffer)) != -1) {
346: result.append(new String(buffer, 0, length));
347: }
348:
349: fis.close();
350:
351: return result.toString();
352: }
353:
354: private void buildRecursiveId(ArrayList idList, File baseDir)
355: throws Exception {
356: File[] subFiles = baseDir.listFiles(new FilenameFilter() {
357: public boolean accept(File dir, String name) {
358: return name.equals(SO6PREFIX);
359: }
360: });
361:
362: if (subFiles.length == 1) {
363: File[] wsId = subFiles[0].listFiles(new FilenameFilter() {
364: public boolean accept(File dir, String name) {
365: return name.equals(SO6_WS_FILE);
366: }
367: });
368:
369: idList.add(0, extractIdFromFile(wsId[0]));
370: }
371:
372: File parentBasePath = baseDir.getParentFile();
373:
374: if (parentBasePath != null) {
375: buildRecursiveId(idList, parentBasePath);
376: }
377: }
378: }
|