隨筆 - 19, 文章 - 1, 評論 - 21, 引用 - 0
          數(shù)據(jù)加載中……

          打造一個基于OSGi的Web Application——使用Tomcat原生API動態(tài)管理Listener

          本文介紹在Tomcat中,如何通過Tomcat原生API實現(xiàn)OSGi容器中動態(tài)管理Listener。

          首先建個接口類,用于描述注冊Listener所使用的Service:
           1 package org.dbstar.osgi.web.register;
           2 
           3 import java.util.EventListener;
           4 
           5 import org.osgi.framework.Bundle;
           6 
           7 public interface ListenerService {
           8     /**
           9      * 判斷當(dāng)前ListenerService實現(xiàn)是否支持使用Listener實例。Service的使用者可以根據(jù)此參數(shù)來選擇適合的調(diào)用方式。
          10      * 
          11      * <pre>
          12      * 1.如果支持使用Listener實例,那么在addApplicationListener(Bundle, ListenerRegistration, EventListener)方法中的
          13      * EventListener實例將被直接注冊到Web Context中去,從而使得Listener實例中的預(yù)設(shè)環(huán)境得以保留。
          14      * 2.如果不支持使用Listener實例,那么不管調(diào)用哪個addApplicationListener方法,新的實例總是會根據(jù)提供的ListenerRegistration
          15      * 創(chuàng)建出來,而方法中的EventListener參數(shù)將被忽略。
          16      * </pre>
          17      * 
          18      * @return 當(dāng)前ListenerService實現(xiàn)是否支持使用Listener實例。
          19      */
          20     public boolean supportsListenerInstance();
          21 
          22     /**
          23      * 根據(jù)提供的ListenerRegistration來注冊一個新的Listener到Web Context中。
          24      * 
          25      * @param bundle 注冊listener的bundle
          26      * @param registration Listener注冊信息
          27      * @return 新增加的Listener的系統(tǒng)名稱,這個名稱用于調(diào)用removeApplicationListener方法
          28      * @exception IllegalArgumentException 如果根據(jù)提供的ListenerRegistration不能創(chuàng)建Listener實例
          29      * @exception IllegalArgumentException 如果待增加的Listener在Web Context中不唯一
          30      */
          31     public String addApplicationListener(Bundle bundle, ListenerRegistration registration);
          32 
          33     /**
          34      * 根據(jù)提供的ListenerRegistration和Listener實例來注冊一個新的Listener到Web Context中。
          35      * 
          36      * <pre>
          37      * 根據(jù)supportsListenerInstance返回的結(jié)果不同,此方法有不同的處理方式:
          38      * 1.如果supportsListenerInstance返回true,那么方法中的Listener實例參數(shù)將被直接注冊到Web Context中去。
          39      * 2.如果supportsListenerInstance返回false,那么方法中的Listener實例參數(shù)將被忽略,Listener實例將根據(jù)ListenerRegistration來創(chuàng)建。
          40      * </pre>
          41      * 
          42      * @param bundle 注冊listener的bundle
          43      * @param registration Listener注冊信息
          44      * @param listener Listener實例
          45      * @return 新增加的Listener的系統(tǒng)名稱,這個名稱用于調(diào)用removeApplicationListener方法
          46      * @exception IllegalArgumentException 如果根據(jù)提供的ListenerRegistration不能創(chuàng)建Listener實例
          47      * @exception IllegalArgumentException 如果待增加的Listener在Web Context中不唯一
          48      */
          49     public String addApplicationListener(Bundle bundle, ListenerRegistration registration, EventListener listener);
          50 
          51     /**
          52      * 從Web Context中刪除指定的Listener。
          53      * 
          54      * @param listenerName 待刪除Listener對應(yīng)的系統(tǒng)名稱,這個名稱通過addApplicationListener方法返回值獲得
          55      */
          56     public void removeApplicationListener(String listenerName);
          57 }

          然后是ListenerRegistration,用于記載注冊Listener時所提供的信息:
           1 package org.dbstar.osgi.web.register;
           2 
           3 import java.util.EventListener;
           4 
           5 public final class ListenerRegistration extends GeneralInfo<ListenerRegistration> {
           6     private static final long serialVersionUID = -6201323709943780698L;
           7 
           8     private Class<? extends EventListener> listenerClass;
           9 
          10     public ListenerRegistration(Class<? extends EventListener> listenerClass) {
          11         this.listenerClass = listenerClass;
          12     }
          13 
          14     public Class<? extends EventListener> getListenerClass() {
          15         return listenerClass;
          16     }
          17 }

           1 package org.dbstar.osgi.web.register;
           2 
           3 import java.io.Serializable;
           4 
           5 public abstract class GeneralInfo<T> implements Serializable {
           6     private static final long serialVersionUID = -5739502239414925563L;
           7 
           8     private String description;
           9     private String displayName;
          10     private Icon icon;
          11 
          12     public final String getDescription() {
          13         return description;
          14     }
          15 
          16     @SuppressWarnings("unchecked")
          17     public final T setDescription(String description) {
          18         this.description = description;
          19         return (T) this;
          20     }
          21 
          22     public final String getDisplayName() {
          23         return displayName;
          24     }
          25 
          26     @SuppressWarnings("unchecked")
          27     public final T setDisplayName(String displayName) {
          28         this.displayName = displayName;
          29         return (T) this;
          30     }
          31 
          32     public final Icon getIcon() {
          33         return icon;
          34     }
          35 
          36     @SuppressWarnings("unchecked")
          37     public final T setIcon(Icon icon) {
          38         this.icon = icon;
          39         return (T) this;
          40     }
          41 }

           1 package org.dbstar.osgi.web.register;
           2 
           3 import java.io.Serializable;
           4 
           5 public final class Icon implements Serializable {
           6     private static final long serialVersionUID = 5232737399172945853L;
           7 
           8     private String smallIcon;
           9     private String largeIcon;
          10 
          11     public String getSmallIcon() {
          12         return smallIcon;
          13     }
          14 
          15     public Icon setSmallIcon(String smallIcon) {
          16         this.smallIcon = smallIcon;
          17         return this;
          18     }
          19 
          20     public String getLargeIcon() {
          21         return largeIcon;
          22     }
          23 
          24     public Icon setLargeIcon(String largeIcon) {
          25         this.largeIcon = largeIcon;
          26         return this;
          27     }
          28 }

          細(xì)心的同學(xué)可能已經(jīng)發(fā)現(xiàn)了,ListenerRegistration類定義的屬性與Servlet 2.4的web.xml中,<listener>標(biāo)簽的描述是一致的。

          好了,Service接口定義好了,下面我們來寫一個實現(xiàn)類,還是使用DS方式:
          ListenerService.xml:
           1 <?xml version="1.0" encoding="UTF-8"?>
           2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="TomcatListenerService"
           3     xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.1.0 http://www.osgi.org/xmlns/scr/v1.1.0/scr.xsd">
           4     <implementation class="org.dbstar.osgi.web.register.tomcat.ListenerServiceImpl" />
           5     <reference cardinality="1..1" interface="org.apache.catalina.Context" name="Context" policy="static" />
           6     <reference bind="bindLog" cardinality="0..1" interface="org.osgi.service.log.LogService" name="LogService"
           7         policy="static" unbind="unbindLog" />
           8     <service>
           9         <provide interface="org.dbstar.osgi.web.register.ListenerService" />
          10     </service>
          11 </scr:component>
          接下來是ListenerServiceImpl,當(dāng)然了,它必須實現(xiàn)ListenerService接口:
            1 package org.dbstar.osgi.web.register.tomcat;
            2 
            3 import java.util.ArrayList;
            4 import java.util.EventListener;
            5 import java.util.HashMap;
            6 import java.util.List;
            7 import java.util.Map;
            8 import java.util.Set;
            9 
           10 import javax.servlet.ServletContextAttributeListener;
           11 import javax.servlet.ServletContextEvent;
           12 import javax.servlet.ServletContextListener;
           13 import javax.servlet.ServletRequestAttributeListener;
           14 import javax.servlet.ServletRequestListener;
           15 import javax.servlet.http.HttpSessionAttributeListener;
           16 import javax.servlet.http.HttpSessionEvent;
           17 import javax.servlet.http.HttpSessionListener;
           18 
           19 import org.apache.catalina.Context;
           20 import org.apache.catalina.Session;
           21 import org.dbstar.osgi.web.register.ListenerRegistration;
           22 import org.dbstar.osgi.web.register.ListenerService;
           23 import org.osgi.framework.Bundle;
           24 import org.osgi.framework.BundleEvent;
           25 import org.osgi.framework.BundleListener;
           26 import org.osgi.service.component.ComponentContext;
           27 import org.osgi.service.log.LogService;
           28 
           29 public class ListenerServiceImpl implements ListenerService, BundleListener {
           30     private Context context;
           31     private LogService log;
           32 
           33     /**
           34      * Bundle ID和ListenerName數(shù)組的映射,通過Bundle ID找到所有此Bundle注冊的Listener的系統(tǒng)名稱
           35      */
           36     private Map<Long, List<String>> bnsMap = new HashMap<Long, List<String>>();
           37 
           38     /**
           39      * ListenerName和Listener實例的映射,通過ListenerName找到對應(yīng)的Listener實例
           40      */
           41     private Map<String, EventListener> nlMap = new HashMap<String, EventListener>();
           42 
           43     /**
           44      * ListenerName和Bundle ID的映射,通過ListenerName找到對應(yīng)的注冊此Listener的Bundle的ID
           45      */
           46     private Map<String, Long> nbMap = new HashMap<String, Long>();
           47 
           48     protected void activate(ComponentContext context) {
           49         // 獲取org.apache.catalina.Context
           50         this.context = (Context) context.locateService("Context");
           51         context.getBundleContext().addBundleListener(this);
           52     }
           53 
           54     protected void deactivate(ComponentContext context) {
           55         context.getBundleContext().removeBundleListener(this);
           56         // 在destroy方法中注銷所有注冊的Listener
           57         destroy();
           58         this.context = null;
           59     }
           60 
           61     protected void bindLog(LogService log) {
           62         this.log = log;
           63     }
           64 
           65     protected void unbindLog(LogService log) {
           66         if (this.log == log) this.log = null;
           67     }
           68 
           69     public void bundleChanged(BundleEvent event) {
           70         if (event.getType() == BundleEvent.STOPPED) {
           71             // 在Bundle Stop之后注銷所有此Bundle注冊的Listener
           72             Long bundleId = event.getBundle().getBundleId();
           73             synchronized (this) {
           74                 if (bnsMap.containsKey(bundleId)) {
           75                     List<String> nameList = bnsMap.get(bundleId);
           76                     for (String listenerName : nameList.toArray(new String[nameList.size()])) {
           77                         removeApplicationListener(bundleId, listenerName);
           78                     }
           79                 }
           80             }
           81         }
           82     }
           83 
           84     /*
           85      * (non-Javadoc)
           86      * 
           87      * @see org.dbstar.osgi.web.register.ListenerService#supportsListenerInstance()
           88      */
           89     public boolean supportsListenerInstance() {
           90         return true;
           91     }
           92 
           93     /*
           94      * (non-Javadoc)
           95      * 
           96      * @see org.dbstar.osgi.web.register.ListenerService#addApplicationListener(org.osgi.framework.Bundle,
           97      * org.dbstar.osgi.web.register.ListenerRegistration)
           98      */
           99     public synchronized String addApplicationListener(Bundle bundle, ListenerRegistration registration) {
          100         EventListener listener = null;
          101         try {
          102             listener = registration.getListenerClass().newInstance();
          103         } catch (Throwable e) {
          104             throw new IllegalArgumentException("Create listener instance error: " + registration.getListenerClass(), e);
          105         }
          106 
          107         return addApplicationListener(bundle, registration, listener);
          108     }
          109 
          110     /*
          111      * (non-Javadoc)
          112      * 
          113      * @see org.dbstar.osgi.web.register.ListenerService#addApplicationListener(org.osgi.framework.Bundle,
          114      * org.dbstar.osgi.web.register.ListenerRegistration, java.util.EventListener)
          115      */
          116     public synchronized String addApplicationListener(Bundle bundle, ListenerRegistration registration,
          117             EventListener listener) {
          118         String listenerName = getListenerName(bundle, listener);
          119 
          120         // 檢查listener是否已經(jīng)注冊過
          121         if (nlMap.containsValue(listener) || nlMap.containsKey(listenerName)) {
          122             if (log != null) log.log(LogService.LOG_ERROR, "Listener already registered: " + listenerName);
          123             throw new IllegalArgumentException("Listener already registered: " + listenerName);
          124         }
          125 
          126         addApplicationListener(bundle.getBundleId(), listener, listenerName);
          127         return listenerName;
          128     }
          129 
          130     private void addApplicationListener(Long bundleId, EventListener listener, String listenerName) {
          131         boolean isApplicationListener = false;
          132 
          133         if (isEventListener(listener)) {
          134             Object[] listeners = context.getApplicationEventListeners();
          135             if (!inArray(listeners, listener)) {
          136                 context.setApplicationEventListeners(addToArray(listeners, listener));
          137                 isApplicationListener = true;
          138             }
          139         }
          140 
          141         if (isLifecycleListener(listener)) {
          142             Object[] listeners = context.getApplicationLifecycleListeners();
          143             if (!inArray(listeners, listener)) {
          144                 context.setApplicationLifecycleListeners(addToArray(listeners, listener));
          145                 isApplicationListener = true;
          146             }
          147         }
          148 
          149         if (isApplicationListener) {
          150             context.addApplicationListener(listenerName);
          151 
          152             List<String> nameList = bnsMap.get(bundleId);
          153             if (nameList == null) {
          154                 nameList = new ArrayList<String>();
          155                 bnsMap.put(bundleId, nameList);
          156             }
          157             nameList.add(listenerName);
          158             nlMap.put(listenerName, listener);
          159             nbMap.put(listenerName, bundleId);
          160 
          161             if (log != null) log.log(LogService.LOG_INFO, "Add ApplicationListener success: " + listenerName);
          162 
          163             // 必要的初始化
          164             if (listener instanceof ServletContextListener) {
          165                 try {
          166                     ServletContextEvent event = new ServletContextEvent(context.getServletContext());
          167                     ((ServletContextListener) listener).contextInitialized(event);
          168                 } catch (Throwable e) {
          169                     if (log != null) log.log(LogService.LOG_WARNING, "contextInitialized() error: " + listenerName, e);
          170                 }
          171             }
          172             if (listener instanceof HttpSessionListener) {
          173                 for (Session session : context.getManager().findSessions()) {
          174                     try {
          175                         HttpSessionEvent event = new HttpSessionEvent(session.getSession());
          176                         ((HttpSessionListener) listener).sessionCreated(event);
          177                     } catch (Throwable e) {
          178                         if (log != null) log.log(LogService.LOG_WARNING, "sessionCreated() error: " + listenerName, e);
          179                     }
          180                 }
          181             }
          182         }
          183     }
          184 
          185     /*
          186      * (non-Javadoc)
          187      * 
          188      * @see org.dbstar.osgi.web.register.ListenerService#removeApplicationListener(java.lang.String)
          189      */
          190     public synchronized void removeApplicationListener(String listenerName) {
          191         // 檢查listener是否已經(jīng)注冊過
          192         if (!nlMap.containsKey(listenerName)) {
          193             if (log != null) log.log(LogService.LOG_WARNING, "Listener not register: " + listenerName);
          194             return;
          195         }
          196 
          197         removeApplicationListener(nbMap.get(listenerName), listenerName);
          198     }
          199 
          200     private void removeApplicationListener(Long bundleId, String listenerName) {
          201         boolean isApplicationListener = false;
          202 
          203         EventListener listener = nlMap.get(listenerName);
          204 
          205         if (isEventListener(listener)) {
          206             Object[] listeners = context.getApplicationEventListeners();
          207             if (inArray(listeners, listener)) {
          208                 context.setApplicationEventListeners(removeFromArray(listeners, listener));
          209                 isApplicationListener = true;
          210             }
          211         }
          212 
          213         if (isLifecycleListener(listener)) {
          214             Object[] listeners = context.getApplicationLifecycleListeners();
          215             if (inArray(listeners, listener)) {
          216                 context.setApplicationLifecycleListeners(removeFromArray(listeners, listener));
          217                 isApplicationListener = true;
          218             }
          219         }
          220 
          221         if (isApplicationListener) {
          222             context.removeApplicationListener(listenerName);
          223 
          224             List<String> nameList = bnsMap.get(bundleId);
          225             if (nameList != null) {
          226                 nameList.remove(listenerName);
          227                 if (nameList.isEmpty()) bnsMap.remove(bundleId);
          228             }
          229             nlMap.remove(listenerName);
          230             nbMap.remove(listenerName);
          231 
          232             if (log != null) log.log(LogService.LOG_INFO, "Remove ApplicationListener success: " + listenerName);
          233 
          234             // destroy
          235             if (listener instanceof ServletContextListener) {
          236                 try {
          237                     ServletContextEvent event = new ServletContextEvent(context.getServletContext());
          238                     ((ServletContextListener) listener).contextDestroyed(event);
          239                 } catch (Throwable e) {
          240                     if (log != null) log.log(LogService.LOG_WARNING, "contextDestroyed() error: " + listenerName, e);
          241                 }
          242             }
          243             if (listener instanceof HttpSessionListener) {
          244                 for (Session session : context.getManager().findSessions()) {
          245                     try {
          246                         HttpSessionEvent event = new HttpSessionEvent(session.getSession());
          247                         ((HttpSessionListener) listener).sessionDestroyed(event);
          248                     } catch (Throwable e) {
          249                         if (log != null) log
          250                                 .log(LogService.LOG_WARNING, "sessionDestroyed() error: " + listenerName, e);
          251                     }
          252                 }
          253             }
          254         }
          255     }
          256 
          257     private static boolean inArray(Object[] objects, Object object) {
          258         for (Object obj : objects) {
          259             if (obj == object) return true;
          260         }
          261         return false;
          262     }
          263 
          264     private static Object[] addToArray(Object[] objects, Object object) {
          265         Object[] newList = new Object[objects.length + 1];
          266         System.arraycopy(objects, 0, newList, 0, objects.length);
          267         newList[objects.length] = object;
          268         return newList;
          269     }
          270 
          271     private static Object[] removeFromArray(Object[] objects, Object object) {
          272         int n = -1;
          273         for (int i = 0; i < objects.length; i++) {
          274             if (objects[i] == object) {
          275                 n = i;
          276                 break;
          277             }
          278         }
          279         if (n < 0return objects;
          280 
          281         // Remove the specified constraint
          282         int j = 0;
          283         Object results[] = new Object[objects.length - 1];
          284         for (int i = 0; i < objects.length; i++) {
          285             if (i != n) results[j++= objects[i];
          286         }
          287         return results;
          288     }
          289 
          290     private String getListenerName(Bundle bundle, EventListener listener) {
          291         Class<? extends EventListener> clazz = listener.getClass();
          292         String className = clazz.getName();
          293         if (bundle != null) className = "bundle@" + bundle.getBundleId() + "@" + className + "@" + listener.hashCode();
          294         return className;
          295     }
          296 
          297     private boolean isEventListener(EventListener listener) {
          298         return (listener instanceof ServletContextAttributeListener)
          299                 || (listener instanceof ServletRequestAttributeListener)
          300                 || (listener instanceof ServletRequestListener) || (listener instanceof HttpSessionAttributeListener);
          301     }
          302 
          303     private boolean isLifecycleListener(EventListener listener) {
          304         return (listener instanceof ServletContextListener) || (listener instanceof HttpSessionListener);
          305     }
          306 
          307     private synchronized void destroy() {
          308         Set<Long> bundleSet = bnsMap.keySet();
          309         for (Long bundleId : bundleSet.toArray(new Long[bundleSet.size()])) {
          310             List<String> nameList = bnsMap.get(bundleId);
          311             for (String listenerName : nameList.toArray(new String[nameList.size()])) {
          312                 removeApplicationListener(bundleId, listenerName);
          313             }
          314         }
          315     }
          316 }
          在這個實現(xiàn)類中,我還實現(xiàn)了一個BundleListener接口,這樣就可以在某個bundle stop的時候,順便把這個bundle所注冊的所有的Listener都給注銷掉,這樣就不用在bundle內(nèi)仔細(xì)的維護(hù)Listener的注冊和注銷了,也算是一個小小的便利吧,當(dāng)然了,對于在Component內(nèi)部注冊的Listener,還是不能在Component銷毀的時候自動實行反注冊,需要自己維護(hù)。


          最后,再給一個使用ListenerService的例子吧:
          listener.xml:
          1 <?xml version="1.0" encoding="UTF-8"?>
          2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="testListener" xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.1.0 http://www.osgi.org/xmlns/scr/v1.1.0/scr.xsd">
          3   <implementation class="testlistener.TestListener"/>
          4   <reference cardinality="1..1" interface="org.dbstar.osgi.web.register.ListenerService" name="ListenerService" policy="static"/>
          5   <reference bind="bindLog" cardinality="0..1" interface="org.osgi.service.log.LogService" name="LogService" policy="static" unbind="unbindLog"/>
          6 </scr:component>

           1 package testlistener;
           2 
           3 import javax.servlet.ServletContextEvent;
           4 import javax.servlet.ServletContextListener;
           5 import javax.servlet.ServletRequestEvent;
           6 import javax.servlet.ServletRequestListener;
           7 
           8 import org.dbstar.osgi.web.register.ListenerRegistration;
           9 import org.dbstar.osgi.web.register.ListenerService;
          10 import org.osgi.framework.Bundle;
          11 import org.osgi.service.component.ComponentContext;
          12 import org.osgi.service.log.LogService;
          13 
          14 public class TestListener implements ServletRequestListener, ServletContextListener {
          15     private LogService log;
          16     private String listenerName;
          17 
          18     protected void activate(ComponentContext context) {
          19         Bundle bundle = context.getBundleContext().getBundle();
          20         ListenerService service = (ListenerService) context.locateService("ListenerService");
          21         listenerName = service.addApplicationListener(bundle, new ListenerRegistration(TestListener.class), this);
          22     }
          23 
          24     protected void deactivate(ComponentContext context) {
          25         ListenerService service = (ListenerService) context.locateService("ListenerService");
          26         if (listenerName != null) service.removeApplicationListener(listenerName);
          27     }
          28 
          29     protected void bindLog(LogService log) {
          30         this.log = log;
          31     }
          32 
          33     protected void unbindLog(LogService log) {
          34         if (this.log == log) this.log = null;
          35     }
          36 
          37     public void requestDestroyed(ServletRequestEvent sre) {
          38         if (log != null) log.log(LogService.LOG_INFO, "requestDestroyed(" + sre + ")");
          39     }
          40 
          41     public void requestInitialized(ServletRequestEvent sre) {
          42         if (log != null) log.log(LogService.LOG_INFO, "requestInitialized(" + sre + ")");
          43     }
          44 
          45     public void contextDestroyed(ServletContextEvent sce) {
          46         if (log != null) log.log(LogService.LOG_INFO, "contextDestroyed(" + sce + ")");
          47     }
          48 
          49     public void contextInitialized(ServletContextEvent sce) {
          50         if (log != null) log.log(LogService.LOG_INFO, "contextInitialized(" + sce + ")");
          51     }
          52 }








          posted on 2010-04-02 18:47 dbstar 閱讀(3665) 評論(2)  編輯  收藏 所屬分類: OSGi

          評論

          # re: 打造一個基于OSGi的Web Application——使用Tomcat原生API動態(tài)管理Listener  回復(fù)  更多評論   

          實現(xiàn)WEB容器的原生API的方案工作量有點大啊,不知道有沒有下文。。。
          2010-05-26 11:55 | hesy

          # re: 打造一個基于OSGi的Web Application——使用Tomcat原生API動態(tài)管理Listener[未登錄]  回復(fù)  更多評論   

          OSGi最成熟、最NB的開源開發(fā)平臺JXADF。
          歡迎訪問:http://osgi.jxtech.net
          2015-11-05 14:09 | Java Fans
          主站蜘蛛池模板: 教育| 鄂托克旗| 苗栗县| 南雄市| 灵璧县| 扎赉特旗| 江油市| 新源县| 睢宁县| 尚志市| 尤溪县| 陆丰市| 射洪县| 岳阳县| 松潘县| 和平县| 富锦市| 河东区| 呈贡县| 铜鼓县| 大名县| 报价| 通海县| 嵊泗县| 镶黄旗| 社旗县| 黑山县| 娄底市| 灵丘县| 宁波市| 万全县| 盖州市| 龙川县| 惠水县| 锡林郭勒盟| 堆龙德庆县| 龙南县| 江达县| 万州区| 江川县| 化州市|