001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.wso2.esb.registry;
021:
022: import org.apache.axiom.om.OMElement;
023: import org.apache.axiom.om.OMNode;
024: import org.apache.axiom.om.impl.builder.StAXOMBuilder;
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.synapse.SynapseException;
028: import org.apache.synapse.registry.AbstractRegistry;
029: import org.apache.synapse.registry.Registry;
030: import org.apache.synapse.registry.RegistryEntry;
031: import org.wso2.esb.ServiceBusConstants;
032: import org.wso2.esb.persistence.PersistenceManager;
033: import org.wso2.esb.persistence.dataobject.RegistryEntryDO;
034:
035: import javax.net.ssl.HostnameVerifier;
036: import javax.net.ssl.HttpsURLConnection;
037: import javax.net.ssl.SSLSession;
038: import javax.xml.stream.XMLInputFactory;
039: import javax.xml.stream.XMLStreamException;
040: import javax.xml.stream.XMLStreamReader;
041: import java.io.BufferedWriter;
042: import java.io.File;
043: import java.io.FileWriter;
044: import java.io.IOException;
045: import java.net.MalformedURLException;
046: import java.net.URL;
047: import java.net.URLConnection;
048: import java.util.ArrayList;
049:
050: /**
051: * Registry implementation for ESB. This assumes that registry resources can be accessed using file
052: * system or http. Registry meta data are accessed using web services.
053: */
054: public class ESBRegistry extends AbstractRegistry implements Registry {
055:
056: public static final int FILE = 100;
057: public static final int HTTP = 101;
058: public static final int HTTPS = 102;
059: public static final String URL_SEPARATOR = "/";
060: public static final char URL_SEPARATOR_CHAR = '/';
061:
062: private static final int DELETE_RETRY_SLEEP_TIME = 10;
063: private static final long DEFAULT_CACHABLE_DURATION = 0;
064: private static final Log log = LogFactory.getLog(ESBRegistry.class);
065:
066: private static final int MAX_KEYS = 200;
067:
068: /**
069: * File system path corresponding to the file url path. This is a system depending path
070: * used for accessing resources as files.
071: */
072: private String localRegistry = null;
073:
074: /**
075: * URL of the WS endpoind to get data about registry entries.
076: */
077: private String metaDataService = null;
078:
079: /**
080: * Specifies whether the registry is in the local host or a remote registry.
081: * Local host means the same computer as ESB is running.
082: */
083: private int registryType = ServiceBusConstants.LOCAL_HOST_REGISTRY;
084:
085: /**
086: * Contains the protocol for the registry. Allowd values are FILE, HTTP and HTTPS.
087: */
088: private int registryProtocol = FILE;
089:
090: public ESBRegistry() {
091: String esbHome = System
092: .getProperty(ServiceBusConstants.ESB_HOME);
093: if (esbHome != null) {
094: this .localRegistry = esbHome;
095: }
096: }
097:
098: public OMNode lookup(String key) {
099:
100: log.info("==> Repository fetch of resource with key : " + key);
101:
102: URLConnection urlc = null;
103: try {
104: URL url = new URL(getRoot() + key);
105: if ("file".equals(url.getProtocol())) {
106: try {
107: url.openStream();
108: } catch (IOException ignored) {
109: if (!localRegistry.endsWith(URL_SEPARATOR)) {
110: localRegistry = localRegistry + URL_SEPARATOR;
111: }
112: url = new URL(url.getProtocol() + ":"
113: + localRegistry + key);
114: try {
115: url.openStream();
116: } catch (IOException e) {
117: return null;
118: }
119: }
120: }
121: urlc = url.openConnection();
122: urlc.connect();
123: } catch (IOException e) {
124: return null;
125: }
126:
127: try {
128: XMLStreamReader parser = XMLInputFactory.newInstance()
129: .createXMLStreamReader(urlc.getInputStream());
130: StAXOMBuilder builder = new StAXOMBuilder(parser);
131: return builder.getDocumentElement();
132:
133: } catch (MalformedURLException e) {
134: handleException("Invalid URL reference " + getRoot() + key,
135: e);
136: } catch (IOException e) {
137: handleException("IO Error reading from URL " + getRoot()
138: + key, e);
139: } catch (XMLStreamException e) {
140: handleException("XML Error reading from URL " + getRoot()
141: + key, e);
142: }
143: return null;
144: }
145:
146: public RegistryEntry getRegistryEntry(String key) {
147:
148: // get information from the actual resource
149: ESBRegistryEntry entry = new ESBRegistryEntry();
150:
151: try {
152: URL url = new URL(getRoot() + key);
153: if ("file".equals(url.getProtocol())) {
154: try {
155: url.openStream();
156: } catch (IOException ignored) {
157: if (!localRegistry.endsWith(URL_SEPARATOR)) {
158: localRegistry = localRegistry + URL_SEPARATOR;
159: }
160: url = new URL(url.getProtocol() + ":"
161: + localRegistry + key);
162: try {
163: url.openStream();
164: } catch (IOException e) {
165: return null;
166: }
167: }
168: }
169: URLConnection urlc = url.openConnection();
170:
171: entry.setKey(key);
172: entry.setName(url.getFile());
173: entry.setType(ServiceBusConstants.file);
174:
175: entry.setDescription("Resource at : " + url.toString());
176: entry.setLastModified(urlc.getLastModified());
177: entry.setVersion(urlc.getLastModified());
178: if (urlc.getExpiration() > 0) {
179: entry.setCachableDuration(urlc.getExpiration()
180: - System.currentTimeMillis());
181: } else {
182: entry.setCachableDuration(getCachableDuration());
183: }
184:
185: } catch (MalformedURLException e) {
186: handleException("Invalid URL reference " + getRoot() + key,
187: e);
188: } catch (IOException e) {
189: handleException("IO Error reading from URL " + getRoot()
190: + key, e);
191: }
192:
193: // get information from the database
194: PersistenceManager persistenceManager = new PersistenceManager();
195: RegistryEntryDO registryEntryDO = persistenceManager
196: .getRegistryEntry(key);
197:
198: if (registryEntryDO != null) {
199:
200: if (registryEntryDO.getExpiryTime() != null) {
201: entry.setCachableDuration(registryEntryDO
202: .getExpiryTime().longValue());
203: } else {
204: entry.setCachableDuration(0);
205: }
206: }
207:
208: return entry;
209: }
210:
211: /**
212: * Updates the metadata of the given registry entry
213: *
214: * @param entry RegistryEntry containing the new metadata
215: */
216: public void updateRegistryEntry(RegistryEntry entry) {
217:
218: RegistryEntryDO registryEntryDO = new RegistryEntryDO();
219: registryEntryDO.setRegistryKey(entry.getKey());
220: registryEntryDO.setExpiryTime(new Long(entry
221: .getCachableDuration()));
222:
223: PersistenceManager persistenceManager = new PersistenceManager();
224: persistenceManager.saveOrUpdateRegistryEntry(registryEntryDO);
225: }
226:
227: /**
228: * Updates the registry resource pointed by the given key.
229: *
230: * @param key Key of the resource to be updated
231: * @param value New value of the resource
232: * @throws Exception
233: */
234: public void updateResource(String key, OMElement value)
235: throws Exception {
236:
237: if (registryType == ServiceBusConstants.LOCAL_HOST_REGISTRY) {
238: File file = new File(localRegistry
239: + getSystemDependentPath(key));
240: if (file.exists()) {
241: BufferedWriter writer = null;
242: try {
243: writer = new BufferedWriter(new FileWriter(file));
244: writer.write(value.toString());
245: writer.flush();
246: } catch (IOException e) {
247: throw new RuntimeException(
248: "Couldn't write to registry entry: " + key);
249: } finally {
250: if (writer != null) {
251: writer.close();
252: }
253: }
254:
255: }
256: } else {
257: throw new RuntimeException(
258: "Remote registry is not supported.");
259: }
260: }
261:
262: /**
263: * Adds a new resource to the registry.
264: *
265: * @param parentName Key of the parent of the new resource
266: * @param resourceName Name of the new resource
267: * @param isLeaf Specifies whether the new resource is a leaf or not. In a file system based
268: * registry, leaf is a file and non-leaf is a folder.
269: * @throws Exception
270: */
271: public void addResource(String parentName, String resourceName,
272: boolean isLeaf) throws Exception {
273:
274: if (registryType == ServiceBusConstants.LOCAL_HOST_REGISTRY) {
275:
276: if (isLeaf) {
277: createFile(getSystemDependentPath(parentName),
278: resourceName);
279: } else {
280: createFolder(getSystemDependentPath(parentName),
281: resourceName);
282: }
283: }
284: }
285:
286: /**
287: * Removes the registry resource identified by the given key. If the key points to a directory,
288: * all its subdirectories and files in those directoris will be deleted. All the database
289: * entries for deleted registry resources will be removed.
290: *
291: * @param key
292: */
293: public void removeResource(String key) {
294:
295: if (registryType == ServiceBusConstants.LOCAL_HOST_REGISTRY) {
296: File resource = new File(localRegistry
297: + getSystemDependentPath(key));
298: if (resource.exists()) {
299:
300: if (resource.isFile()) {
301: deleteFile(resource);
302: } else if (resource.isDirectory()) {
303: deleteDirectory(resource);
304: }
305:
306: } else {
307: throw new SynapseException("Parent folder: " + key
308: + " does not exists.");
309: }
310: }
311: }
312:
313: /**
314: * Returns the registry key in URL style (with "/" as the separator) for a given file.
315: *
316: * @param file File to get the key
317: * @return Registry key
318: */
319: private String getRegsitryKey(File file) {
320:
321: String path = file.getAbsolutePath();
322: String rootPath = new File(localRegistry).getAbsolutePath();
323: return getURLPath(path.substring(rootPath.length() + 1)); // we have remove the preceeding "/"
324: }
325:
326: private void deleteFile(File file) {
327:
328: boolean success = file.delete();
329: if (!success) {
330: // try with this work around to overcome a known bug in windows
331: // work around:
332: // run garbage collector and sleep for some time and delete
333: // if still didn't delete,
334: // rename file to a temp file in "temp" dir and mark it to delete on exist
335:
336: System.gc();
337: try {
338: Thread.sleep(DELETE_RETRY_SLEEP_TIME);
339: } catch (InterruptedException e) {
340: // ignore the exception
341: }
342:
343: success = file.delete();
344: if (!success) {
345: int suffix = 1;
346: File renamedFile = null;
347:
348: File tempDir = new File("temp");
349: if (!tempDir.exists()) {
350: tempDir.mkdir();
351: }
352:
353: do {
354: String changedName = "d" + suffix + file.getName();
355: renamedFile = new File(tempDir, changedName);
356: suffix++;
357: } while (renamedFile.exists());
358:
359: if (file.renameTo(renamedFile)) {
360: success = true;
361: renamedFile.deleteOnExit();
362: } else {
363: throw new RuntimeException(
364: "Cannot delete the resource: "
365: + file.getName());
366: }
367: }
368: }
369:
370: if (success) {
371: PersistenceManager persistenceManager = new PersistenceManager();
372: persistenceManager
373: .deleteRegistryEntry(getRegsitryKey(file));
374: }
375: }
376:
377: private void deleteDirectory(File dir) {
378:
379: File[] children = dir.listFiles();
380: for (int i = 0; i < children.length; i++) {
381: if (children[i].isFile()) {
382: deleteFile(children[i]);
383: } else if (children[i].isDirectory()) {
384: deleteDirectory(children[i]);
385: }
386: }
387:
388: boolean success = dir.delete();
389: if (success) {
390: PersistenceManager persistenceManager = new PersistenceManager();
391: persistenceManager.deleteRegistryEntry(getRegsitryKey(dir));
392: } else {
393: throw new RuntimeException("Cannot delete the resource: "
394: + dir.getName());
395: }
396: }
397:
398: private void createFolder(String parentName, String newFolderName)
399: throws Exception {
400:
401: /*
402: search for parent. if found, create the new folder in it. this depends on whether we are
403: using a remote file system or not.
404: add entry 'parentName/newFolderName' as key to the database.
405: */
406:
407: if (registryType == ServiceBusConstants.LOCAL_HOST_REGISTRY) {
408: File parent = new File(localRegistry + parentName);
409: if (parent.exists()) {
410:
411: File newEntry = new File(parent, newFolderName);
412:
413: boolean success = newEntry.mkdir();
414: if (!success) {
415: throw new Exception("Couldn't create folder: "
416: + newFolderName);
417: }
418:
419: // update meta data to the database
420: // note that we are using the registry key part (path without the local registry
421: // root as the key in db
422: // if the new folder has a parent folder, use its values for defaults for all
423: // possible properties
424:
425: RegistryEntryDO registryEntryDO = new RegistryEntryDO();
426: registryEntryDO.setRegistryKey(parentName
427: + URL_SEPARATOR + newFolderName);
428:
429: PersistenceManager persistenceManager = new PersistenceManager();
430: RegistryEntryDO parentEntryDO = persistenceManager
431: .getRegistryEntry(getURLPath(parentName));
432: if (parentEntryDO != null) {
433: registryEntryDO.setExpiryTime(parentEntryDO
434: .getExpiryTime());
435: } else {
436: registryEntryDO.setExpiryTime(new Long(
437: getCachableDuration()));
438: }
439:
440: persistenceManager.addRegistryEntry(registryEntryDO);
441:
442: } else {
443: throw new SynapseException("Parent folder: "
444: + parentName + " does not exists.");
445: }
446: }
447: }
448:
449: private void createFile(String parentName, String newFileName)
450: throws Exception {
451:
452: /*
453: search for parent. if found, create the new folder in it. this depends on whether we are
454: using a remote file system or not.
455: */
456:
457: if (registryType == ServiceBusConstants.LOCAL_HOST_REGISTRY) {
458: File parent = new File(localRegistry + parentName);
459: if (parent.exists()) {
460:
461: File newFile = new File(parent, newFileName);
462: boolean success = newFile.createNewFile();
463: if (!success) {
464: throw new Exception("Couldn't create resource: "
465: + newFileName);
466: }
467:
468: // update meta data to the database
469: // note that we are using the registry key part (path without the local registry
470: // root as the key in db
471: // update the db only if we have some thing else to write other than the key
472:
473: RegistryEntryDO registryEntryDO = new RegistryEntryDO();
474: registryEntryDO.setRegistryKey(parentName
475: + URL_SEPARATOR + newFileName);
476:
477: PersistenceManager persistenceManager = new PersistenceManager();
478: RegistryEntryDO parentEntryDO = persistenceManager
479: .getRegistryEntry(getURLPath(parentName));
480: if (parentEntryDO != null) {
481: registryEntryDO.setExpiryTime(parentEntryDO
482: .getExpiryTime());
483: } else {
484: registryEntryDO.setExpiryTime(new Long(
485: getCachableDuration()));
486: }
487:
488: persistenceManager.addRegistryEntry(registryEntryDO);
489:
490: } else {
491: throw new SynapseException("Parent folder: "
492: + parentName + " does not exists.");
493: }
494: }
495: }
496:
497: /**
498: * Configure the ESB registry using registry parameters.
499: *
500: * root: file:directory - registry is on local host
501: * directory is used to access metadata
502: *
503: * root: http/https:location - has to specify one of the following settings
504: * localRegistry - location of the local registry
505: * metadataService - url of the service to access metadata
506: * If none of above parameters are given "registry" folder is taken as the local registry.
507: *
508: * @param name
509: * @param value
510: */
511: public void addConfigProperty(String name, String value) {
512:
513: if (localRegistry == null) {
514: // registry root should always end with "/"
515: if (ServiceBusConstants.LOCAL_REGISTRY_ROOT
516: .endsWith(URL_SEPARATOR)) {
517: localRegistry = ServiceBusConstants.LOCAL_REGISTRY_ROOT;
518: } else {
519: localRegistry = ServiceBusConstants.LOCAL_REGISTRY_ROOT
520: + URL_SEPARATOR;
521: }
522: }
523: if (name != null && value != null) {
524: if (name.equals("root")) {
525:
526: // root should always end with '/'
527: // therefore, property keys do not have to begin with '/', which could be misleading
528: try {
529: URL url = new URL(value);
530: if ("file".equals(url.getProtocol())) {
531: try {
532: url.openStream();
533: } catch (IOException ignored) {
534: if (!localRegistry.endsWith(URL_SEPARATOR)) {
535: localRegistry = localRegistry
536: + URL_SEPARATOR;
537: }
538: url = new URL(url.getProtocol() + ":"
539: + localRegistry + url.getPath());
540: try {
541: url.openStream();
542: } catch (IOException e) {
543: log.error(e);
544: }
545: }
546: }
547: if (url.getProtocol().equals("file")) {
548: registryProtocol = FILE;
549:
550: registryType = ServiceBusConstants.LOCAL_HOST_REGISTRY;
551:
552: if (url.getPath().endsWith(URL_SEPARATOR)) {
553: localRegistry = getSystemDependentPath(url
554: .getPath());
555: } else {
556: localRegistry = getSystemDependentPath(url
557: .getPath())
558: + File.separator;
559: }
560:
561: } else if (url.getProtocol().equals("http")) {
562: registryProtocol = HTTP;
563: } else if (url.getProtocol().equals("https")) {
564: registryProtocol = HTTPS;
565: }
566:
567: if (!value.endsWith(URL_SEPARATOR)) {
568: value = value + URL_SEPARATOR;
569: }
570:
571: } catch (MalformedURLException e) {
572: // don't set the root if this is not a valid URL
573: throw new RuntimeException(
574: "Registry root should be a valid URL.");
575: }
576: }
577:
578: if (name.equals("localRegistry")) {
579: registryType = ServiceBusConstants.LOCAL_HOST_REGISTRY;
580:
581: // registry root always ends with "/"
582: if (!value.endsWith(File.separator)) {
583: value = value + File.separator;
584: }
585: localRegistry = value;
586: }
587:
588: if (name.equals("matadataService")) {
589: registryType = ServiceBusConstants.REMOTE_HOST_REGISTRY;
590: metaDataService = value;
591: }
592:
593: // check if host name verification is disabled
594: if (name.equalsIgnoreCase("disableHostNameVerification")) {
595: if (value.equalsIgnoreCase("true")) {
596: HostnameVerifier hv = new HostnameVerifier() {
597: public boolean verify(String urlHostName,
598: SSLSession session) {
599: return true;
600: }
601: };
602:
603: HttpsURLConnection.setDefaultHostnameVerifier(hv);
604: }
605: }
606:
607: super .addConfigProperty(name, value);
608: } else {
609: log.debug("Name and Value must need");
610: }
611: }
612:
613: /**
614: * Returns the root of the registry. This is always a URL.
615: *
616: * @return Registry root.
617: */
618: public String getRoot() {
619: String root = (String) properties.get("root");
620: if (root == null) {
621: return "";
622: } else {
623: return root;
624: }
625: }
626:
627: public long getCachableDuration() {
628: String cachableDuration = (String) properties
629: .get("cachableDuration");
630: return cachableDuration == null ? DEFAULT_CACHABLE_DURATION
631: : Long.parseLong(cachableDuration);
632: }
633:
634: /**
635: * Gives the children of the given entry. If the registry is in the same host get the children
636: * (subfolders and files) using the file system. If the registry is in a remote host, get
637: * children using a WS call. Give null or registry entry with "" as the key to list the children
638: * of the root.
639: *
640: * @param entry
641: * @return children of the given entry
642: */
643: public RegistryEntry[] getChildren(RegistryEntry entry) {
644:
645: String registryRoot = localRegistry;
646:
647: if (entry == null) {
648: // give the children of the root
649: // null or key = "" stands for root
650:
651: ESBRegistryEntry urlEntry = new ESBRegistryEntry();
652: urlEntry.setKey("");
653: entry = urlEntry;
654: }
655:
656: if (registryType == ServiceBusConstants.LOCAL_HOST_REGISTRY) {
657:
658: // registry is in the local file system. access it directly.
659:
660: String entryPath = getSystemDependentPath(entry.getKey());
661: File file = new File(registryRoot + entryPath);
662: if (!file.isDirectory()) {
663: return null;
664: }
665:
666: try {
667:
668: String[] children = file.list();
669: RegistryEntry[] entries = new RegistryEntry[children.length];
670: for (int i = 0; i < children.length; i++) {
671:
672: ESBRegistryEntry registryEntry = new ESBRegistryEntry();
673: if (entry.getKey().equals("")) {
674: // user asking for the children of the root
675: registryEntry.setKey(children[i]);
676: } else {
677: if (entryPath.endsWith(URL_SEPARATOR)) {
678: registryEntry.setKey(getURLPath(entry
679: .getKey()
680: + children[i]));
681: } else {
682: registryEntry.setKey(getURLPath(entry
683: .getKey()
684: + URL_SEPARATOR + children[i]));
685: }
686: }
687:
688: // set if the registry entry is a file or a folder
689: File entryFile = new File(registryRoot
690: + getSystemDependentPath(registryEntry
691: .getKey()));
692: if (entryFile.isDirectory()) {
693: registryEntry
694: .setType(ServiceBusConstants.folder);
695: }
696:
697: entries[i] = registryEntry;
698: }
699:
700: return entries;
701:
702: } catch (Exception e) {
703: throw new SynapseException("Error in reading the URL.");
704: }
705:
706: } else if (registryType == ServiceBusConstants.REMOTE_HOST_REGISTRY) {
707: // implement for remote registries.
708: }
709:
710: return null;
711: }
712:
713: public RegistryEntry[] getDescendants(RegistryEntry entry) {
714:
715: ArrayList list = new ArrayList();
716: RegistryEntry[] entries = getChildren(entry);
717: if (entries != null) {
718: for (int i = 0; i < entries.length; i++) {
719:
720: if (list.size() > MAX_KEYS) {
721: break;
722: }
723:
724: fillDescendants(entries[i], list);
725: }
726: }
727:
728: RegistryEntry[] descendants = new RegistryEntry[list.size()];
729: for (int i = 0; i < list.size(); i++) {
730: descendants[i] = (RegistryEntry) list.get(i);
731: }
732:
733: return descendants;
734: }
735:
736: private void fillDescendants(RegistryEntry parent, ArrayList list) {
737:
738: RegistryEntry[] entries = getChildren(parent);
739: if (entries != null) {
740: for (int i = 0; i < entries.length; i++) {
741:
742: if (list.size() > MAX_KEYS) {
743: break;
744: }
745:
746: fillDescendants(entries[i], list);
747: }
748: } else {
749: list.add(parent);
750: }
751: }
752:
753: private String getSystemDependentPath(String path) {
754: String filePath = path.replace(URL_SEPARATOR_CHAR,
755: File.separatorChar);
756: return filePath;
757: }
758:
759: private String getURLPath(String filePath) {
760: return filePath.replace(File.separatorChar, URL_SEPARATOR_CHAR);
761: }
762:
763: private void handleException(String msg, Exception e) {
764: log.error(msg, e);
765: throw new SynapseException(msg, e);
766: }
767: }
|