mirror of https://github.com/apache/lucene.git
SOLR-1106 -- Made CoreAdminHandler Actions pluggable so that additional actions may be plugged in or the existing ones can be overridden if needed.
git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@768165 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e0fb06b436
commit
33bcabc9f6
|
@ -205,6 +205,9 @@ New Features
|
|||
FieldAnalysisRequestHandler and DocumentAnalysisRequestHandler is also provided in the Solrj client.
|
||||
(Uri Boness, shalin)
|
||||
|
||||
38. SOLR-1106: Made CoreAdminHandler Actions pluggable so that additional actions may be plugged in or the existing
|
||||
ones can be overridden if needed. (Kay Kay, Noble Paul, shalin)
|
||||
|
||||
Optimizations
|
||||
----------------------
|
||||
1. SOLR-374: Use IndexReader.reopen to save resources by re-using parts of the
|
||||
|
|
|
@ -174,6 +174,7 @@ public class CoreContainer
|
|||
persistent = cfg.getBool( "solr/@persistent", false );
|
||||
libDir = cfg.get( "solr/@sharedLib", null);
|
||||
adminPath = cfg.get( "solr/cores/@adminPath", null );
|
||||
String adminHandler = cfg.get( "solr/cores/@adminHandler", null );
|
||||
managementPath = cfg.get("solr/cores/@managementPath", null );
|
||||
|
||||
if (libDir != null) {
|
||||
|
@ -182,8 +183,12 @@ public class CoreContainer
|
|||
libLoader = SolrResourceLoader.createClassLoader(f, null);
|
||||
}
|
||||
|
||||
if( adminPath != null ) {
|
||||
coreAdminHandler = this.createMultiCoreHandler();
|
||||
if (adminPath != null) {
|
||||
if (adminHandler == null) {
|
||||
coreAdminHandler = new CoreAdminHandler(this);
|
||||
} else {
|
||||
coreAdminHandler = this.createMultiCoreHandler(adminHandler);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -506,13 +511,16 @@ public class CoreContainer
|
|||
* Creates a CoreAdminHandler for this MultiCore.
|
||||
* @return a CoreAdminHandler
|
||||
*/
|
||||
protected CoreAdminHandler createMultiCoreHandler() {
|
||||
return new CoreAdminHandler() {
|
||||
@Override
|
||||
public CoreContainer getCoreContainer() {
|
||||
return CoreContainer.this;
|
||||
}
|
||||
};
|
||||
protected CoreAdminHandler createMultiCoreHandler(final String adminHandlerClass) {
|
||||
SolrResourceLoader loader = new SolrResourceLoader(null, libLoader, null);
|
||||
Object obj = loader.newAdminHandlerInstance(CoreContainer.this, adminHandlerClass);
|
||||
if ( !(obj instanceof CoreAdminHandler))
|
||||
{
|
||||
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
|
||||
"adminHandlerClass is not of type "+ CoreAdminHandler.class );
|
||||
|
||||
}
|
||||
return (CoreAdminHandler) obj;
|
||||
}
|
||||
|
||||
public CoreAdminHandler getMultiCoreHandler() {
|
||||
|
|
|
@ -348,6 +348,33 @@ public class SolrResourceLoader implements ResourceLoader
|
|||
return obj;
|
||||
}
|
||||
|
||||
public Object newAdminHandlerInstance(final CoreContainer coreContainer, String cname, String ... subpackages) {
|
||||
Class clazz = findClass(cname,subpackages);
|
||||
if( clazz == null ) {
|
||||
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
|
||||
"Can not find class: "+cname + " in " + classLoader, false);
|
||||
}
|
||||
|
||||
Object obj = null;
|
||||
try {
|
||||
Constructor ctor = clazz.getConstructor(CoreContainer.class);
|
||||
obj = ctor.newInstance(coreContainer);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
|
||||
"Error instantiating class: '" + clazz.getName()+"'", e, false );
|
||||
}
|
||||
//TODO: Does SolrCoreAware make sense here since in a multi-core context
|
||||
// which core are we talking about ?
|
||||
if( obj instanceof ResourceLoaderAware ) {
|
||||
assertAwareCompatibility( ResourceLoaderAware.class, obj );
|
||||
waitingForResources.add( (ResourceLoaderAware)obj );
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Object newInstance(String cName, String [] subPackages, Class[] params, Object[] args){
|
||||
Class clazz = findClass(cName,subPackages);
|
||||
if( clazz == null ) {
|
||||
|
|
|
@ -17,197 +17,365 @@
|
|||
|
||||
package org.apache.solr.handler.admin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.params.CoreAdminParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.core.CoreDescriptor;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.handler.RequestHandlerBase;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrQueryResponse;
|
||||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
import org.apache.solr.util.RefCounted;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @version $Id$
|
||||
* @since solr 1.3
|
||||
*/
|
||||
public abstract class CoreAdminHandler extends RequestHandlerBase
|
||||
{
|
||||
public CoreAdminHandler()
|
||||
{
|
||||
public class CoreAdminHandler extends RequestHandlerBase {
|
||||
protected final CoreContainer coreContainer;
|
||||
|
||||
public CoreAdminHandler() {
|
||||
super();
|
||||
// Unlike most request handlers, CoreContainer initialization
|
||||
// should happen in the constructor...
|
||||
this.coreContainer = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overloaded ctor to inject CoreContainer into the handler.
|
||||
*
|
||||
* @param coreContainer Core Container of the solr webapp installed.
|
||||
*/
|
||||
public CoreAdminHandler(final CoreContainer coreContainer) {
|
||||
this.coreContainer = coreContainer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
final public void init(NamedList args) {
|
||||
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
|
||||
"CoreAdminHandler should not be configured in solrconf.xml\n"+
|
||||
"it is a special Handler configured directly by the RequestDispatcher" );
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||
"CoreAdminHandler should not be configured in solrconf.xml\n" +
|
||||
"it is a special Handler configured directly by the RequestDispatcher");
|
||||
}
|
||||
|
||||
/**
|
||||
* The instance of CoreContainer this handler handles.
|
||||
* This should be the CoreContainer instance that created this handler.
|
||||
* The instance of CoreContainer this handler handles. This should be the CoreContainer instance that created this
|
||||
* handler.
|
||||
*
|
||||
* @return a CoreContainer instance
|
||||
*/
|
||||
public abstract CoreContainer getCoreContainer();
|
||||
public CoreContainer getCoreContainer() {
|
||||
return this.coreContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
|
||||
{
|
||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
|
||||
// Make sure the cores is enabled
|
||||
CoreContainer cores = getCoreContainer();
|
||||
if( cores == null ) {
|
||||
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Core container instance missing" );
|
||||
if (cores == null) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Core container instance missing");
|
||||
}
|
||||
boolean do_persist = false;
|
||||
boolean doPersist = false;
|
||||
|
||||
// Pick the action
|
||||
SolrParams params = req.getParams();
|
||||
SolrParams required = params.required();
|
||||
CoreAdminAction action = CoreAdminAction.STATUS;
|
||||
String a = params.get( CoreAdminParams.ACTION );
|
||||
if( a != null ) {
|
||||
action = CoreAdminAction.get( a );
|
||||
if( action == null ) {
|
||||
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Unknown 'action' value. Use: "+CoreAdminAction.values() );
|
||||
String a = params.get(CoreAdminParams.ACTION);
|
||||
if (a != null) {
|
||||
action = CoreAdminAction.get(a);
|
||||
if (action == null) {
|
||||
doPersist = this.handleCustomAction(req, rsp);
|
||||
}
|
||||
}
|
||||
String cname = params.get( CoreAdminParams.CORE );
|
||||
|
||||
switch(action) {
|
||||
case CREATE: {
|
||||
String name = params.get(CoreAdminParams.NAME);
|
||||
CoreDescriptor dcore = new CoreDescriptor(cores, name, params.get(CoreAdminParams.INSTANCE_DIR));
|
||||
|
||||
// fillup optional parameters
|
||||
String opts = params.get(CoreAdminParams.CONFIG);
|
||||
if (opts != null)
|
||||
dcore.setConfigName(opts);
|
||||
|
||||
opts = params.get(CoreAdminParams.SCHEMA);
|
||||
if (opts != null)
|
||||
dcore.setSchemaName(opts);
|
||||
|
||||
opts = params.get(CoreAdminParams.DATA_DIR);
|
||||
if (opts != null)
|
||||
dcore.setDataDir(opts);
|
||||
|
||||
SolrCore core = cores.create(dcore);
|
||||
cores.register(name, core,false);
|
||||
rsp.add("core", core.getName());
|
||||
do_persist = cores.isPersistent();
|
||||
break;
|
||||
}
|
||||
|
||||
case RENAME: {
|
||||
String name = params.get(CoreAdminParams.OTHER);
|
||||
if (cname.equals(name)) break;
|
||||
|
||||
SolrCore core = cores.getCore(cname);
|
||||
if (core != null) {
|
||||
do_persist = cores.isPersistent();
|
||||
cores.register(name, core, false);
|
||||
cores.remove(cname);
|
||||
core.close();
|
||||
if (action != null) {
|
||||
switch (action) {
|
||||
case CREATE: {
|
||||
doPersist = this.handleCreateAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ALIAS: {
|
||||
String name = params.get(CoreAdminParams.OTHER);
|
||||
if (cname.equals(name)) break;
|
||||
|
||||
SolrCore core = cores.getCore(cname);
|
||||
if (core != null) {
|
||||
do_persist = cores.isPersistent();
|
||||
cores.register(name, core, false);
|
||||
// no core.close() since each entry in the cores map should increase the ref
|
||||
case RENAME: {
|
||||
doPersist = this.handleRenameAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UNLOAD: {
|
||||
SolrCore core = cores.remove(cname);
|
||||
core.close();
|
||||
do_persist = cores.isPersistent();
|
||||
break;
|
||||
}
|
||||
|
||||
case STATUS: {
|
||||
NamedList<Object> status = new SimpleOrderedMap<Object>();
|
||||
if( cname == null ) {
|
||||
for (String name : cores.getCoreNames()) {
|
||||
status.add(name, getCoreStatus( cores, name ) );
|
||||
}
|
||||
case ALIAS: {
|
||||
doPersist = this.handleAliasAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
status.add(cname, getCoreStatus( cores, cname ) );
|
||||
|
||||
case UNLOAD: {
|
||||
doPersist = this.handleUnloadAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
rsp.add( "status", status );
|
||||
do_persist = false; // no state change
|
||||
break;
|
||||
|
||||
}
|
||||
case STATUS: {
|
||||
doPersist = this.handleStatusAction(req, rsp);
|
||||
break;
|
||||
|
||||
case PERSIST: {
|
||||
String fileName = params.get( CoreAdminParams.FILE );
|
||||
if (fileName != null) {
|
||||
File file = new File(cores.getConfigFile().getParentFile(), fileName);
|
||||
cores.persistFile(file);
|
||||
rsp.add("saved", file.getAbsolutePath());
|
||||
do_persist = false;
|
||||
}
|
||||
else if (!cores.isPersistent()) {
|
||||
throw new SolrException (SolrException.ErrorCode.FORBIDDEN, "Persistence is not enabled");
|
||||
|
||||
case PERSIST: {
|
||||
doPersist = this.handlePersistAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
else
|
||||
do_persist = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case RELOAD: {
|
||||
cores.reload( cname );
|
||||
do_persist = false; // no change on reload
|
||||
break;
|
||||
}
|
||||
case RELOAD: {
|
||||
doPersist = this.handleReloadAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
case SWAP: {
|
||||
do_persist = params.getBool(CoreAdminParams.PERSISTENT, cores.isPersistent());
|
||||
String other = required.get( CoreAdminParams.OTHER );
|
||||
cores.swap( cname, other );
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
|
||||
"TODO: IMPLEMENT: " + action );
|
||||
}
|
||||
} // switch
|
||||
case SWAP: {
|
||||
doPersist = this.handleSwapAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
doPersist = this.handleCustomAction(req, rsp);
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
}
|
||||
// Should we persist the changes?
|
||||
if (do_persist) {
|
||||
if (doPersist) {
|
||||
cores.persist();
|
||||
rsp.add("saved", cores.getConfigFile().getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private static NamedList<Object> getCoreStatus(CoreContainer cores, String cname ) throws IOException
|
||||
{
|
||||
/**
|
||||
* Handle Custom Action.
|
||||
* <p/>
|
||||
* This method could be overridden by derived classes to handle custom actions. <br> By default - this method throws a
|
||||
* solr exception. Derived classes are free to write their derivation if necessary.
|
||||
*/
|
||||
protected boolean handleCustomAction(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unsupported operation: " +
|
||||
req.getParams().get(CoreAdminParams.ACTION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle 'CREATE' action.
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return persistence flag as necessary.
|
||||
*
|
||||
* @throws SolrException in case of a configuration error.
|
||||
*/
|
||||
protected boolean handleCreateAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
|
||||
try {
|
||||
SolrParams params = req.getParams();
|
||||
String name = params.get(CoreAdminParams.NAME);
|
||||
CoreDescriptor dcore = new CoreDescriptor(coreContainer, name, params.get(CoreAdminParams.INSTANCE_DIR));
|
||||
|
||||
// fillup optional parameters
|
||||
String opts = params.get(CoreAdminParams.CONFIG);
|
||||
if (opts != null)
|
||||
dcore.setConfigName(opts);
|
||||
|
||||
opts = params.get(CoreAdminParams.SCHEMA);
|
||||
if (opts != null)
|
||||
dcore.setSchemaName(opts);
|
||||
|
||||
opts = params.get(CoreAdminParams.DATA_DIR);
|
||||
if (opts != null)
|
||||
dcore.setDataDir(opts);
|
||||
|
||||
SolrCore core = coreContainer.create(dcore);
|
||||
coreContainer.register(name, core, false);
|
||||
rsp.add("core", core.getName());
|
||||
return coreContainer.isPersistent();
|
||||
} catch (Exception ex) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
"Error executing default implementation of CREATE", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "RENAME" Action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws SolrException
|
||||
*/
|
||||
protected boolean handleRenameAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
|
||||
SolrParams params = req.getParams();
|
||||
|
||||
String name = params.get(CoreAdminParams.OTHER);
|
||||
String cname = params.get(CoreAdminParams.CORE);
|
||||
boolean doPersist = false;
|
||||
|
||||
if (cname.equals(name)) return doPersist;
|
||||
|
||||
SolrCore core = coreContainer.getCore(cname);
|
||||
if (core != null) {
|
||||
doPersist = coreContainer.isPersistent();
|
||||
coreContainer.register(name, core, false);
|
||||
coreContainer.remove(cname);
|
||||
core.close();
|
||||
}
|
||||
return doPersist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "ALIAS" action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean handleAliasAction(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
SolrParams params = req.getParams();
|
||||
|
||||
String name = params.get(CoreAdminParams.OTHER);
|
||||
String cname = params.get(CoreAdminParams.CORE);
|
||||
boolean doPersist = false;
|
||||
if (cname.equals(name)) return doPersist;
|
||||
|
||||
SolrCore core = coreContainer.getCore(cname);
|
||||
if (core != null) {
|
||||
doPersist = coreContainer.isPersistent();
|
||||
coreContainer.register(name, core, false);
|
||||
// no core.close() since each entry in the cores map should increase the ref
|
||||
}
|
||||
return doPersist;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle "UNLOAD" Action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean handleUnloadAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
|
||||
SolrParams params = req.getParams();
|
||||
String cname = params.get(CoreAdminParams.CORE);
|
||||
SolrCore core = coreContainer.remove(cname);
|
||||
core.close();
|
||||
return coreContainer.isPersistent();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "STATUS" action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean handleStatusAction(SolrQueryRequest req, SolrQueryResponse rsp)
|
||||
throws SolrException {
|
||||
SolrParams params = req.getParams();
|
||||
|
||||
String cname = params.get(CoreAdminParams.CORE);
|
||||
boolean doPersist = false;
|
||||
NamedList<Object> status = new SimpleOrderedMap<Object>();
|
||||
try {
|
||||
if (cname == null) {
|
||||
for (String name : coreContainer.getCoreNames()) {
|
||||
status.add(name, getCoreStatus(coreContainer, name));
|
||||
}
|
||||
} else {
|
||||
status.add(cname, getCoreStatus(coreContainer, cname));
|
||||
}
|
||||
rsp.add("status", status);
|
||||
doPersist = false; // no state change
|
||||
return doPersist;
|
||||
} catch (Exception ex) {
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||
"Error handling 'status' action ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler "PERSIST" action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws SolrException
|
||||
*/
|
||||
protected boolean handlePersistAction(SolrQueryRequest req, SolrQueryResponse rsp)
|
||||
throws SolrException {
|
||||
SolrParams params = req.getParams();
|
||||
boolean doPersist = false;
|
||||
String fileName = params.get(CoreAdminParams.FILE);
|
||||
if (fileName != null) {
|
||||
File file = new File(coreContainer.getConfigFile().getParentFile(), fileName);
|
||||
coreContainer.persistFile(file);
|
||||
rsp.add("saved", file.getAbsolutePath());
|
||||
doPersist = false;
|
||||
} else if (!coreContainer.isPersistent()) {
|
||||
throw new SolrException(SolrException.ErrorCode.FORBIDDEN, "Persistence is not enabled");
|
||||
} else
|
||||
doPersist = true;
|
||||
|
||||
return doPersist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler "RELOAD" action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean handleReloadAction(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
SolrParams params = req.getParams();
|
||||
String cname = params.get(CoreAdminParams.CORE);
|
||||
try {
|
||||
coreContainer.reload(cname);
|
||||
return false; // no change on reload
|
||||
} catch (Exception ex) {
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error handling 'reload' action", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "SWAP" action
|
||||
*
|
||||
* @param req
|
||||
* @param rsp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean handleSwapAction(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
final SolrParams params = req.getParams();
|
||||
final SolrParams required = params.required();
|
||||
|
||||
final String cname = params.get(CoreAdminParams.CORE);
|
||||
boolean doPersist = params.getBool(CoreAdminParams.PERSISTENT, coreContainer.isPersistent());
|
||||
String other = required.get(CoreAdminParams.OTHER);
|
||||
coreContainer.swap(cname, other);
|
||||
return doPersist;
|
||||
|
||||
}
|
||||
|
||||
protected static NamedList<Object> getCoreStatus(CoreContainer cores, String cname) throws IOException {
|
||||
NamedList<Object> info = new SimpleOrderedMap<Object>();
|
||||
SolrCore core = cores.getCore(cname);
|
||||
if (core != null) {
|
||||
|
@ -227,7 +395,7 @@ public abstract class CoreAdminHandler extends RequestHandlerBase
|
|||
return info;
|
||||
}
|
||||
|
||||
private static String normalizePath(String path) {
|
||||
protected static String normalizePath(String path) {
|
||||
if (path == null)
|
||||
return null;
|
||||
path = path.replace('/', File.separatorChar);
|
||||
|
|
Loading…
Reference in New Issue