1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.eclipse.jetty.server.session;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.sql.Connection;
29 import java.sql.PreparedStatement;
30 import java.sql.ResultSet;
31 import java.sql.SQLException;
32 import java.sql.Statement;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.ListIterator;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.concurrent.atomic.AtomicReference;
40
41 import javax.servlet.SessionTrackingMode;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.servlet.http.HttpSessionEvent;
44 import javax.servlet.http.HttpSessionListener;
45
46 import org.eclipse.jetty.server.SessionIdManager;
47 import org.eclipse.jetty.server.handler.ContextHandler;
48 import org.eclipse.jetty.util.log.Log;
49 import org.eclipse.jetty.util.log.Logger;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class JDBCSessionManager extends AbstractSessionManager
76 {
77 private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
78
79 private ConcurrentHashMap<String, AbstractSession> _sessions;
80 protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
81 protected long _saveIntervalSec = 60;
82
83
84
85
86
87
88
89
90
91 public class Session extends AbstractSession
92 {
93 private static final long serialVersionUID = 5208464051134226143L;
94
95
96
97
98 private boolean _dirty=false;
99
100
101
102
103
104 private long _cookieSet;
105
106
107
108
109
110 private long _expiryTime;
111
112
113
114
115
116 private long _lastSaved;
117
118
119
120
121
122 private String _lastNode;
123
124
125
126
127
128 private String _virtualHost;
129
130
131
132
133
134 private String _rowId;
135
136
137
138
139
140 private String _canonicalContext;
141
142
143
144
145
146
147
148 protected Session (HttpServletRequest request)
149 {
150 super(JDBCSessionManager.this,request);
151 int maxInterval=getMaxInactiveInterval();
152 _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
153 _virtualHost = JDBCSessionManager.getVirtualHost(_context);
154 _canonicalContext = canonicalize(_context.getContextPath());
155 _lastNode = getSessionIdManager().getWorkerName();
156 }
157
158
159
160
161
162
163
164
165
166 protected Session (String sessionId, String rowId, long created, long accessed)
167 {
168 super(JDBCSessionManager.this, created, accessed, sessionId);
169 _rowId = rowId;
170 }
171
172
173 protected synchronized String getRowId()
174 {
175 return _rowId;
176 }
177
178 protected synchronized void setRowId(String rowId)
179 {
180 _rowId = rowId;
181 }
182
183 public synchronized void setVirtualHost (String vhost)
184 {
185 _virtualHost=vhost;
186 }
187
188 public synchronized String getVirtualHost ()
189 {
190 return _virtualHost;
191 }
192
193 public synchronized long getLastSaved ()
194 {
195 return _lastSaved;
196 }
197
198 public synchronized void setLastSaved (long time)
199 {
200 _lastSaved=time;
201 }
202
203 public synchronized void setExpiryTime (long time)
204 {
205 _expiryTime=time;
206 }
207
208 public synchronized long getExpiryTime ()
209 {
210 return _expiryTime;
211 }
212
213
214 public synchronized void setCanonicalContext(String str)
215 {
216 _canonicalContext=str;
217 }
218
219 public synchronized String getCanonicalContext ()
220 {
221 return _canonicalContext;
222 }
223
224 public void setCookieSet (long ms)
225 {
226 _cookieSet = ms;
227 }
228
229 public synchronized long getCookieSet ()
230 {
231 return _cookieSet;
232 }
233
234 public synchronized void setLastNode (String node)
235 {
236 _lastNode=node;
237 }
238
239 public synchronized String getLastNode ()
240 {
241 return _lastNode;
242 }
243
244 @Override
245 public void setAttribute (String name, Object value)
246 {
247 super.setAttribute(name, value);
248 _dirty=true;
249 }
250
251 @Override
252 public void removeAttribute (String name)
253 {
254 super.removeAttribute(name);
255 _dirty=true;
256 }
257
258 @Override
259 protected void cookieSet()
260 {
261 _cookieSet = getAccessed();
262 }
263
264
265
266
267
268
269
270 @Override
271 protected boolean access(long time)
272 {
273 synchronized (this)
274 {
275 if (super.access(time))
276 {
277 int maxInterval=getMaxInactiveInterval();
278 _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
279 return true;
280 }
281 return false;
282 }
283 }
284
285
286
287
288
289
290
291 @Override
292 protected void complete()
293 {
294 synchronized (this)
295 {
296 super.complete();
297 try
298 {
299 if (isValid())
300 {
301 if (_dirty)
302 {
303
304
305 willPassivate();
306 updateSession(this);
307 didActivate();
308 }
309 else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
310 {
311 updateSessionAccessTime(this);
312 }
313 }
314 }
315 catch (Exception e)
316 {
317 LOG.warn("Problem persisting changed session data id="+getId(), e);
318 }
319 finally
320 {
321 _dirty=false;
322 }
323 }
324 }
325
326 @Override
327 protected void timeout() throws IllegalStateException
328 {
329 if (LOG.isDebugEnabled())
330 LOG.debug("Timing out session id="+getClusterId());
331 super.timeout();
332 }
333
334 @Override
335 public String toString ()
336 {
337 return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
338 ",created="+getCreationTime()+",accessed="+getAccessed()+
339 ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
340 ",lastSaved="+_lastSaved+",expiry="+_expiryTime;
341 }
342 }
343
344
345
346
347
348
349
350
351
352 protected class ClassLoadingObjectInputStream extends ObjectInputStream
353 {
354 public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
355 {
356 super(in);
357 }
358
359 public ClassLoadingObjectInputStream () throws IOException
360 {
361 super();
362 }
363
364 @Override
365 public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
366 {
367 try
368 {
369 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
370 }
371 catch (ClassNotFoundException e)
372 {
373 return super.resolveClass(cl);
374 }
375 }
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397 public void setSaveInterval (long sec)
398 {
399 _saveIntervalSec=sec;
400 }
401
402 public long getSaveInterval ()
403 {
404 return _saveIntervalSec;
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420 public void cacheInvalidate (Session session)
421 {
422
423 }
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442 @Override
443 public Session getSession(String idInCluster)
444 {
445 Session session = null;
446 Session memSession = (Session)_sessions.get(idInCluster);
447
448 synchronized (this)
449 {
450
451
452
453
454
455
456
457
458
459 long now = System.currentTimeMillis();
460 if (LOG.isDebugEnabled())
461 {
462 if (memSession==null)
463 LOG.debug("getSession("+idInCluster+"): not in session map,"+
464 " now="+now+
465 " lastSaved="+(memSession==null?0:memSession._lastSaved)+
466 " interval="+(_saveIntervalSec * 1000L));
467 else
468 LOG.debug("getSession("+idInCluster+"): in session map, "+
469 " now="+now+
470 " lastSaved="+(memSession==null?0:memSession._lastSaved)+
471 " interval="+(_saveIntervalSec * 1000L)+
472 " lastNode="+memSession._lastNode+
473 " thisNode="+getSessionIdManager().getWorkerName()+
474 " difference="+(now - memSession._lastSaved));
475 }
476
477 try
478 {
479 if (memSession==null)
480 {
481 LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
482 session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
483 }
484 else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
485 {
486 LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
487 session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
488 }
489 else
490 {
491 LOG.debug("getSession("+idInCluster+"): session in session map");
492 session = memSession;
493 }
494 }
495 catch (Exception e)
496 {
497 LOG.warn("Unable to load session "+idInCluster, e);
498 return null;
499 }
500
501
502
503 if (session != null)
504 {
505
506 if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
507 {
508
509 if (session._expiryTime <= 0 || session._expiryTime > now)
510 {
511 if (LOG.isDebugEnabled())
512 LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
513
514 session.setLastNode(getSessionIdManager().getWorkerName());
515 _sessions.put(idInCluster, session);
516
517
518 try
519 {
520 updateSessionNode(session);
521 session.didActivate();
522 }
523 catch (Exception e)
524 {
525 LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
526 return null;
527 }
528 }
529 else
530 {
531 LOG.debug("getSession ({}): Session has expired", idInCluster);
532 session=null;
533 }
534
535 }
536 else
537 {
538
539 session = memSession;
540 LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
541 }
542 }
543 else
544 {
545
546 LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
547 }
548
549 return session;
550 }
551 }
552
553
554
555
556
557
558 @Override
559 public int getSessions()
560 {
561 int size = 0;
562 synchronized (this)
563 {
564 size = _sessions.size();
565 }
566 return size;
567 }
568
569
570
571
572
573
574
575 @Override
576 public void doStart() throws Exception
577 {
578 if (_sessionIdManager==null)
579 throw new IllegalStateException("No session id manager defined");
580
581 _jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
582
583 _sessions = new ConcurrentHashMap<String, AbstractSession>();
584
585 super.doStart();
586 }
587
588
589
590
591
592
593
594 @Override
595 public void doStop() throws Exception
596 {
597 _sessions.clear();
598 _sessions = null;
599
600 super.doStop();
601 }
602
603 @Override
604 protected void invalidateSessions()
605 {
606
607
608
609
610
611
612 }
613
614
615
616
617
618
619
620 protected void invalidateSession (String idInCluster)
621 {
622 Session session = null;
623 synchronized (this)
624 {
625 session = (Session)_sessions.get(idInCluster);
626 }
627
628 if (session != null)
629 {
630 session.invalidate();
631 }
632 }
633
634
635
636
637
638
639
640 @Override
641 protected boolean removeSession(String idInCluster)
642 {
643 synchronized (this)
644 {
645 Session session = (Session)_sessions.remove(idInCluster);
646 try
647 {
648 if (session != null)
649 deleteSession(session);
650 }
651 catch (Exception e)
652 {
653 LOG.warn("Problem deleting session id="+idInCluster, e);
654 }
655 return session!=null;
656 }
657 }
658
659
660
661
662
663
664
665 @Override
666 protected void addSession(AbstractSession session)
667 {
668 if (session==null)
669 return;
670
671 synchronized (this)
672 {
673 _sessions.put(session.getClusterId(), session);
674 }
675
676
677
678 try
679 {
680 synchronized (session)
681 {
682 session.willPassivate();
683 storeSession(((JDBCSessionManager.Session)session));
684 session.didActivate();
685 }
686 }
687 catch (Exception e)
688 {
689 LOG.warn("Unable to store new session id="+session.getId() , e);
690 }
691 }
692
693
694
695
696
697
698
699 @Override
700 protected AbstractSession newSession(HttpServletRequest request)
701 {
702 return new Session(request);
703 }
704
705
706
707
708
709
710
711 @Override
712 public void removeSession(AbstractSession session, boolean invalidate)
713 {
714
715 boolean removed = false;
716
717 synchronized (this)
718 {
719
720 if (getSession(session.getClusterId()) != null)
721 {
722 removed = true;
723 removeSession(session.getClusterId());
724 }
725 }
726
727 if (removed)
728 {
729
730 _sessionIdManager.removeSession(session);
731
732 if (invalidate)
733 _sessionIdManager.invalidateAll(session.getClusterId());
734
735 if (invalidate && !_sessionListeners.isEmpty())
736 {
737 HttpSessionEvent event=new HttpSessionEvent(session);
738 for (HttpSessionListener l : _sessionListeners)
739 l.sessionDestroyed(event);
740 }
741 if (!invalidate)
742 {
743 session.willPassivate();
744 }
745 }
746 }
747
748
749
750
751
752
753
754
755 protected void expire (List<?> sessionIds)
756 {
757
758 if (isStopping() || isStopped())
759 return;
760
761
762 Thread thread=Thread.currentThread();
763 ClassLoader old_loader=thread.getContextClassLoader();
764 ListIterator<?> itor = sessionIds.listIterator();
765
766 try
767 {
768 while (itor.hasNext())
769 {
770 String sessionId = (String)itor.next();
771 if (LOG.isDebugEnabled())
772 LOG.debug("Expiring session id "+sessionId);
773
774 Session session = (Session)_sessions.get(sessionId);
775 if (session != null)
776 {
777 session.timeout();
778 itor.remove();
779 }
780 else
781 {
782 if (LOG.isDebugEnabled())
783 LOG.debug("Unrecognized session id="+sessionId);
784 }
785 }
786 }
787 catch (Throwable t)
788 {
789 LOG.warn("Problem expiring sessions", t);
790 }
791 finally
792 {
793 thread.setContextClassLoader(old_loader);
794 }
795 }
796
797
798
799
800
801
802
803
804 protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
805 throws Exception
806 {
807 final AtomicReference<Session> _reference = new AtomicReference<Session>();
808 final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
809 Runnable load = new Runnable()
810 {
811 @SuppressWarnings("unchecked")
812 public void run()
813 {
814 Session session = null;
815 Connection connection=null;
816 PreparedStatement statement = null;
817 try
818 {
819 connection = getConnection();
820 statement = _jdbcSessionIdMgr._dbAdaptor.getLoadStatement(connection, id, canonicalContextPath, vhost);
821 ResultSet result = statement.executeQuery();
822 if (result.next())
823 {
824 session = new Session(id, result.getString(_jdbcSessionIdMgr._sessionTableRowId), result.getLong("createTime"), result.getLong("accessTime"));
825 session.setCookieSet(result.getLong("cookieTime"));
826 session.setLastAccessedTime(result.getLong("lastAccessTime"));
827 session.setLastNode(result.getString("lastNode"));
828 session.setLastSaved(result.getLong("lastSavedTime"));
829 session.setExpiryTime(result.getLong("expiryTime"));
830 session.setCanonicalContext(result.getString("contextPath"));
831 session.setVirtualHost(result.getString("virtualHost"));
832
833 InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, "map");
834 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
835 Object o = ois.readObject();
836 session.addAttributes((Map<String,Object>)o);
837 ois.close();
838
839 if (LOG.isDebugEnabled())
840 LOG.debug("LOADED session "+session);
841 }
842 _reference.set(session);
843 }
844 catch (Exception e)
845 {
846 _exception.set(e);
847 }
848 finally
849 {
850 if (statement!=null)
851 {
852 try { statement.close(); }
853 catch(Exception e) { LOG.warn(e); }
854 }
855
856 if (connection!=null)
857 {
858 try { connection.close();}
859 catch(Exception e) { LOG.warn(e); }
860 }
861 }
862 }
863 };
864
865 if (_context==null)
866 load.run();
867 else
868 _context.getContextHandler().handle(load);
869
870 if (_exception.get()!=null)
871 {
872
873
874 _jdbcSessionIdMgr.removeSession(id);
875 throw _exception.get();
876 }
877
878 return _reference.get();
879 }
880
881
882
883
884
885
886
887 protected void storeSession (Session session)
888 throws Exception
889 {
890 if (session==null)
891 return;
892
893
894 Connection connection = getConnection();
895 PreparedStatement statement = null;
896 try
897 {
898 String rowId = calculateRowId(session);
899
900 long now = System.currentTimeMillis();
901 connection.setAutoCommit(true);
902 statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession);
903 statement.setString(1, rowId);
904 statement.setString(2, session.getId());
905 statement.setString(3, session.getCanonicalContext());
906 statement.setString(4, session.getVirtualHost());
907 statement.setString(5, getSessionIdManager().getWorkerName());
908 statement.setLong(6, session.getAccessed());
909 statement.setLong(7, session.getLastAccessedTime());
910 statement.setLong(8, session.getCreationTime());
911 statement.setLong(9, session.getCookieSet());
912 statement.setLong(10, now);
913 statement.setLong(11, session.getExpiryTime());
914
915 ByteArrayOutputStream baos = new ByteArrayOutputStream();
916 ObjectOutputStream oos = new ObjectOutputStream(baos);
917 oos.writeObject(session.getAttributeMap());
918 byte[] bytes = baos.toByteArray();
919
920 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
921 statement.setBinaryStream(12, bais, bytes.length);
922
923 statement.executeUpdate();
924 session.setRowId(rowId);
925 session.setLastSaved(now);
926
927
928 if (LOG.isDebugEnabled())
929 LOG.debug("Stored session "+session);
930 }
931 finally
932 {
933 if (statement!=null)
934 {
935 try { statement.close(); }
936 catch(Exception e) { LOG.warn(e); }
937 }
938
939 if (connection!=null)
940 connection.close();
941 }
942 }
943
944
945
946
947
948
949
950
951 protected void updateSession (Session data)
952 throws Exception
953 {
954 if (data==null)
955 return;
956
957 Connection connection = getConnection();
958 PreparedStatement statement = null;
959 try
960 {
961 long now = System.currentTimeMillis();
962 connection.setAutoCommit(true);
963 statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession);
964 statement.setString(1, getSessionIdManager().getWorkerName());
965 statement.setLong(2, data.getAccessed());
966 statement.setLong(3, data.getLastAccessedTime());
967 statement.setLong(4, now);
968 statement.setLong(5, data.getExpiryTime());
969
970 ByteArrayOutputStream baos = new ByteArrayOutputStream();
971 ObjectOutputStream oos = new ObjectOutputStream(baos);
972 oos.writeObject(data.getAttributeMap());
973 byte[] bytes = baos.toByteArray();
974 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
975
976 statement.setBinaryStream(6, bais, bytes.length);
977 statement.setString(7, data.getRowId());
978 statement.executeUpdate();
979
980 data.setLastSaved(now);
981 if (LOG.isDebugEnabled())
982 LOG.debug("Updated session "+data);
983 }
984 finally
985 {
986 if (statement!=null)
987 {
988 try { statement.close(); }
989 catch(Exception e) { LOG.warn(e); }
990 }
991
992 if (connection!=null)
993 connection.close();
994 }
995 }
996
997
998
999
1000
1001
1002
1003
1004 protected void updateSessionNode (Session data)
1005 throws Exception
1006 {
1007 String nodeId = getSessionIdManager().getWorkerName();
1008 Connection connection = getConnection();
1009 PreparedStatement statement = null;
1010 try
1011 {
1012 connection.setAutoCommit(true);
1013 statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode);
1014 statement.setString(1, nodeId);
1015 statement.setString(2, data.getRowId());
1016 statement.executeUpdate();
1017 statement.close();
1018 if (LOG.isDebugEnabled())
1019 LOG.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
1020 }
1021 finally
1022 {
1023 if (statement!=null)
1024 {
1025 try { statement.close(); }
1026 catch(Exception e) { LOG.warn(e); }
1027 }
1028
1029 if (connection!=null)
1030 connection.close();
1031 }
1032 }
1033
1034
1035
1036
1037
1038
1039
1040 private void updateSessionAccessTime (Session data)
1041 throws Exception
1042 {
1043 Connection connection = getConnection();
1044 PreparedStatement statement = null;
1045 try
1046 {
1047 long now = System.currentTimeMillis();
1048 connection.setAutoCommit(true);
1049 statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime);
1050 statement.setString(1, getSessionIdManager().getWorkerName());
1051 statement.setLong(2, data.getAccessed());
1052 statement.setLong(3, data.getLastAccessedTime());
1053 statement.setLong(4, now);
1054 statement.setLong(5, data.getExpiryTime());
1055 statement.setString(6, data.getRowId());
1056 statement.executeUpdate();
1057 data.setLastSaved(now);
1058 statement.close();
1059 if (LOG.isDebugEnabled())
1060 LOG.debug("Updated access time session id="+data.getId());
1061 }
1062 finally
1063 {
1064 if (statement!=null)
1065 {
1066 try { statement.close(); }
1067 catch(Exception e) { LOG.warn(e); }
1068 }
1069
1070 if (connection!=null)
1071 connection.close();
1072 }
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085 protected void deleteSession (Session data)
1086 throws Exception
1087 {
1088 Connection connection = getConnection();
1089 PreparedStatement statement = null;
1090 try
1091 {
1092 connection.setAutoCommit(true);
1093 statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession);
1094 statement.setString(1, data.getRowId());
1095 statement.executeUpdate();
1096 if (LOG.isDebugEnabled())
1097 LOG.debug("Deleted Session "+data);
1098 }
1099 finally
1100 {
1101 if (statement!=null)
1102 {
1103 try { statement.close(); }
1104 catch(Exception e) { LOG.warn(e); }
1105 }
1106
1107 if (connection!=null)
1108 connection.close();
1109 }
1110 }
1111
1112
1113
1114
1115
1116
1117
1118
1119 private Connection getConnection ()
1120 throws SQLException
1121 {
1122 return ((JDBCSessionIdManager)getSessionIdManager()).getConnection();
1123 }
1124
1125
1126
1127
1128
1129
1130
1131
1132 private String calculateRowId (Session data)
1133 {
1134 String rowId = canonicalize(_context.getContextPath());
1135 rowId = rowId + "_" + getVirtualHost(_context);
1136 rowId = rowId+"_"+data.getId();
1137 return rowId;
1138 }
1139
1140
1141
1142
1143
1144
1145
1146
1147 private static String getVirtualHost (ContextHandler.Context context)
1148 {
1149 String vhost = "0.0.0.0";
1150
1151 if (context==null)
1152 return vhost;
1153
1154 String [] vhosts = context.getContextHandler().getVirtualHosts();
1155 if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
1156 return vhost;
1157
1158 return vhosts[0];
1159 }
1160
1161
1162
1163
1164
1165
1166
1167 private static String canonicalize (String path)
1168 {
1169 if (path==null)
1170 return "";
1171
1172 return path.replace('/', '_').replace('.','_').replace('\\','_');
1173 }
1174 }